thread_tools 0.27 → 0.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/thread_tools/debugmutex.rb +6 -1
- data/lib/thread_tools/threadpool.rb +38 -34
- data/test/test_threadpool.rb +34 -17
- data/thread_tools.gemspec +1 -1
- metadata +2 -2
|
@@ -31,6 +31,11 @@ require 'thread'
|
|
|
31
31
|
module ThreadTools
|
|
32
32
|
|
|
33
33
|
class EMutexOrder < Exception
|
|
34
|
+
attr_reader :mutex
|
|
35
|
+
def initialize(mutex, str)
|
|
36
|
+
@mutex = mutex
|
|
37
|
+
super(str)
|
|
38
|
+
end
|
|
34
39
|
end
|
|
35
40
|
|
|
36
41
|
class DebugMutex < Mutex
|
|
@@ -80,7 +85,7 @@ module ThreadTools
|
|
|
80
85
|
else
|
|
81
86
|
if @owner[:locks].delete(self)
|
|
82
87
|
@out_of_order_locks += 1
|
|
83
|
-
raise EMutexOrder.new("Expected #{@owner[:locks].last}")
|
|
88
|
+
raise EMutexOrder.new(self, "Expected #{@owner[:locks].last}")
|
|
84
89
|
end
|
|
85
90
|
# if called again let it pass
|
|
86
91
|
end
|
|
@@ -33,6 +33,7 @@ module ThreadTools
|
|
|
33
33
|
# if set to true a new worker is created if the pool is empty
|
|
34
34
|
attr_accessor :create_on_spawn
|
|
35
35
|
|
|
36
|
+
|
|
36
37
|
# *_size* should be at least 1, *_thr_group* (optional) thread group
|
|
37
38
|
def initialize(_size, _thr_group = nil)
|
|
38
39
|
@kill_worker_on_exception = false
|
|
@@ -40,7 +41,8 @@ module ThreadTools
|
|
|
40
41
|
@pool_mtx = Mutex.new
|
|
41
42
|
@pool_cv = ConditionVariable.new
|
|
42
43
|
@pool = []
|
|
43
|
-
@
|
|
44
|
+
@dummy_grp = ThreadGroup.new
|
|
45
|
+
@busy_grp = _thr_group.nil? ? ThreadGroup.new : _thr_group
|
|
44
46
|
@create_on_spawn = false
|
|
45
47
|
_size = 1 if _size < 1
|
|
46
48
|
_size.times { create_worker }
|
|
@@ -48,35 +50,31 @@ module ThreadTools
|
|
|
48
50
|
|
|
49
51
|
def create_worker
|
|
50
52
|
Thread.new do
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
rescue
|
|
73
|
-
if (@kill_worker_on_exception)
|
|
74
|
-
break # exit thread on exception
|
|
53
|
+
@pool_mtx.synchronize do @size += 1 end
|
|
54
|
+
begin
|
|
55
|
+
thr = Thread.current
|
|
56
|
+
thr[:jobs] = [] # XXX array not really necessary
|
|
57
|
+
thr[:sem] = Semaphore.new(0)
|
|
58
|
+
loop do
|
|
59
|
+
@pool_mtx.synchronize do
|
|
60
|
+
@pool << thr # puts this thread in the pool
|
|
61
|
+
@pool_cv.signal
|
|
62
|
+
end
|
|
63
|
+
thr[:sem].acquire # wait here for jobs to become available
|
|
64
|
+
job = thr[:jobs].shift # pop out a job
|
|
65
|
+
if (job.nil? || job[:block].nil?)
|
|
66
|
+
break # exit thread if job or block is nil
|
|
67
|
+
end
|
|
68
|
+
begin
|
|
69
|
+
@busy_grp.add(thr) # adds the thread
|
|
70
|
+
job[:block].call(*job[:args]) # call block
|
|
71
|
+
@dummy_grp.add(thr) # removes from the previous group
|
|
72
|
+
rescue
|
|
73
|
+
break if @kill_worker_on_exception # exit thread on exception
|
|
75
74
|
end
|
|
76
75
|
end
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
@size -= 1
|
|
76
|
+
ensure
|
|
77
|
+
@pool_mtx.synchronize do @size -= 1 end
|
|
80
78
|
end
|
|
81
79
|
end
|
|
82
80
|
end
|
|
@@ -85,9 +83,7 @@ module ThreadTools
|
|
|
85
83
|
thr = nil
|
|
86
84
|
@pool_mtx.synchronize do
|
|
87
85
|
# creates a new worker thread if pool is empty and flag is set
|
|
88
|
-
if
|
|
89
|
-
create_worker
|
|
90
|
-
end
|
|
86
|
+
create_worker if @create_on_spawn && @pool.empty?
|
|
91
87
|
# wait here until a worker is available
|
|
92
88
|
@pool_cv.wait(@pool_mtx) until !(thr = @pool.shift).nil?
|
|
93
89
|
thr[:jobs] << { :args => args, :block => block }
|
|
@@ -105,10 +101,18 @@ module ThreadTools
|
|
|
105
101
|
thr[:jobs].clear # clear any pending job
|
|
106
102
|
thr[:jobs] << nil # queue a nil job
|
|
107
103
|
thr[:sem].release
|
|
108
|
-
if
|
|
109
|
-
thr.join # wait here for the thread to die
|
|
110
|
-
end
|
|
104
|
+
thr.join if _sync # wait here for the thread to die
|
|
111
105
|
end
|
|
106
|
+
busy_list = @busy_grp.list
|
|
107
|
+
busy_list.each do |athr|
|
|
108
|
+
athr.raise('shutdown') # XXX we could .kill instead
|
|
109
|
+
athr.join if _sync # wait here for the thread to die
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# returns the number of busy workers
|
|
114
|
+
def busy_size
|
|
115
|
+
@busy_grp.list.size
|
|
112
116
|
end
|
|
113
117
|
end
|
|
114
118
|
|
data/test/test_threadpool.rb
CHANGED
|
@@ -6,36 +6,53 @@ class ThreadPoolTest < Test::Unit::TestCase
|
|
|
6
6
|
def setup
|
|
7
7
|
super
|
|
8
8
|
@tpool = ThreadTools::ThreadPool.new(2)
|
|
9
|
-
@tpool.kill_worker_on_exception = true
|
|
10
9
|
end
|
|
11
10
|
|
|
12
|
-
def
|
|
11
|
+
def test_spawn
|
|
13
12
|
uniq_threads = {}
|
|
14
13
|
sum_orig = 0
|
|
15
|
-
sum_lock = Mutex.new
|
|
16
14
|
sum_thr = 0
|
|
17
|
-
|
|
15
|
+
sum_lock = Mutex.new
|
|
16
|
+
4.times do |i|
|
|
18
17
|
sum_orig += i
|
|
19
|
-
@tpool.spawn(i)
|
|
20
|
-
sum_lock.synchronize
|
|
21
|
-
sum_thr += ti
|
|
22
|
-
end
|
|
18
|
+
@tpool.spawn(i) do |j|
|
|
19
|
+
sum_lock.synchronize { sum_thr += j }
|
|
23
20
|
uniq_threads[Thread.current] = true
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
raise "oups"
|
|
27
|
-
end
|
|
28
|
-
}
|
|
29
|
-
}
|
|
21
|
+
end
|
|
22
|
+
end
|
|
30
23
|
sleep 0.1
|
|
31
|
-
# 2 workers - 1 from raised exceptions = 1
|
|
32
|
-
assert_equal(@tpool.size, 1)
|
|
33
|
-
@tpool.shutdown
|
|
34
24
|
# same sum
|
|
35
25
|
assert_equal(sum_orig, sum_thr)
|
|
36
26
|
# number of different threads
|
|
37
27
|
assert_equal(uniq_threads.size, 2)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_raise
|
|
31
|
+
@tpool.kill_worker_on_exception = true
|
|
32
|
+
2.times do |i|
|
|
33
|
+
@tpool.spawn(i+1) do |j|
|
|
34
|
+
raise 'oups' if j == 2
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
sleep 0.1
|
|
38
|
+
# 2 workers - 1 from raised exceptions = 1
|
|
39
|
+
assert_equal(@tpool.size, 1)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_shutdown
|
|
43
|
+
# initial number of threads
|
|
44
|
+
assert_equal(@tpool.size, 2)
|
|
45
|
+
@tpool.shutdown
|
|
38
46
|
# after shutdown all threads are dead
|
|
39
47
|
assert_equal(@tpool.size, 0)
|
|
40
48
|
end
|
|
49
|
+
|
|
50
|
+
def test_spawn_create
|
|
51
|
+
# kill all workers first
|
|
52
|
+
@tpool.shutdown
|
|
53
|
+
@tpool.create_on_spawn = true
|
|
54
|
+
@tpool.spawn {}
|
|
55
|
+
# a new worker was created
|
|
56
|
+
assert_equal(@tpool.size, 1)
|
|
57
|
+
end
|
|
41
58
|
end
|
data/thread_tools.gemspec
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: thread_tools
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: "0.
|
|
4
|
+
version: "0.28"
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Tralamazza
|
|
@@ -9,7 +9,7 @@ autorequire:
|
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
|
|
12
|
-
date: 2009-10-
|
|
12
|
+
date: 2009-10-14 00:00:00 +02:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies: []
|
|
15
15
|
|