thread_tools 0.27 → 0.28
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|