dat-worker-pool 0.1.0 → 0.2.0
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/dat-worker-pool.gemspec +1 -0
- data/lib/dat-worker-pool.rb +61 -32
- data/lib/dat-worker-pool/queue.rb +5 -7
- data/lib/dat-worker-pool/version.rb +1 -1
- data/lib/dat-worker-pool/worker.rb +25 -21
- data/test/unit/dat-worker-pool_tests.rb +40 -6
- data/test/unit/queue_tests.rb +31 -18
- data/test/unit/worker_tests.rb +94 -10
- metadata +10 -10
data/dat-worker-pool.gemspec
CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |gem|
|
|
11
11
|
gem.description = "A simple thread pool for processing generic 'work'"
|
12
12
|
gem.summary = "A simple thread pool for processing generic 'work'"
|
13
13
|
gem.homepage = "http://github.com/redding/dat-worker-pool"
|
14
|
+
gem.license = 'MIT'
|
14
15
|
|
15
16
|
gem.files = `git ls-files`.split($/)
|
16
17
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
data/lib/dat-worker-pool.rb
CHANGED
@@ -37,6 +37,22 @@ class DatWorkerPool
|
|
37
37
|
@workers_waiting.count
|
38
38
|
end
|
39
39
|
|
40
|
+
def worker_available?
|
41
|
+
!reached_max_workers? || @workers_waiting.count > 0
|
42
|
+
end
|
43
|
+
|
44
|
+
def all_spawned_workers_are_busy?
|
45
|
+
@workers_waiting.count <= 0
|
46
|
+
end
|
47
|
+
|
48
|
+
def reached_max_workers?
|
49
|
+
@mutex.synchronize{ @spawned >= @max_workers }
|
50
|
+
end
|
51
|
+
|
52
|
+
def queue_empty?
|
53
|
+
@queue.empty?
|
54
|
+
end
|
55
|
+
|
40
56
|
# Check if all workers are busy before adding the work. When the work is
|
41
57
|
# added, a worker will stop waiting (if it was idle). Because of that, we
|
42
58
|
# can't reliably check if all workers are busy. We might think all workers are
|
@@ -44,9 +60,9 @@ class DatWorkerPool
|
|
44
60
|
# would spawn a worker to do nothing.
|
45
61
|
def add_work(work_item)
|
46
62
|
return if work_item.nil?
|
47
|
-
new_worker_needed =
|
63
|
+
new_worker_needed = all_spawned_workers_are_busy?
|
48
64
|
@queue.push work_item
|
49
|
-
self.spawn_worker if new_worker_needed &&
|
65
|
+
self.spawn_worker if new_worker_needed && !reached_max_workers?
|
50
66
|
end
|
51
67
|
|
52
68
|
# Shutdown each worker and then the queue. Shutting down the queue will
|
@@ -55,9 +71,9 @@ class DatWorkerPool
|
|
55
71
|
# finish.
|
56
72
|
# **NOTE** Any work that is left on the queue isn't processed. The controlling
|
57
73
|
# application for the worker pool should gracefully handle these items.
|
58
|
-
def shutdown(timeout)
|
74
|
+
def shutdown(timeout = nil)
|
59
75
|
begin
|
60
|
-
|
76
|
+
proc = OptionalTimeoutProc.new(timeout, true) do
|
61
77
|
@workers.each(&:shutdown)
|
62
78
|
@queue.shutdown
|
63
79
|
|
@@ -68,51 +84,44 @@ class DatWorkerPool
|
|
68
84
|
# `each`).
|
69
85
|
@workers.first.join until @workers.empty?
|
70
86
|
end
|
87
|
+
proc.call
|
71
88
|
rescue TimeoutError => exception
|
72
89
|
exception.message.replace "Timed out shutting down the worker pool"
|
73
90
|
@debug ? raise(exception) : self.logger.error(exception.message)
|
74
91
|
end
|
75
92
|
end
|
76
93
|
|
77
|
-
# public, because workers need to call it for themselves
|
78
|
-
def despawn_worker(worker)
|
79
|
-
@mutex.synchronize do
|
80
|
-
@spawned -= 1
|
81
|
-
@workers.delete worker
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
94
|
protected
|
86
95
|
|
87
96
|
def spawn_worker
|
88
97
|
@mutex.synchronize do
|
89
|
-
|
90
|
-
do_work(work_item)
|
98
|
+
Worker.new(@queue).tap do |w|
|
99
|
+
w.on_work = proc{ |work_item| do_work(work_item) }
|
100
|
+
w.on_waiting = proc{ @workers_waiting.increment }
|
101
|
+
w.on_continuing = proc{ @workers_waiting.decrement }
|
102
|
+
w.on_shutdown = proc{ |worker| despawn_worker(worker) }
|
103
|
+
|
104
|
+
@workers << w
|
105
|
+
@spawned += 1
|
106
|
+
|
107
|
+
w.start
|
91
108
|
end
|
92
|
-
@workers << worker
|
93
|
-
@spawned += 1
|
94
|
-
worker
|
95
109
|
end
|
96
110
|
end
|
97
111
|
|
98
|
-
def
|
99
|
-
|
100
|
-
@
|
101
|
-
|
102
|
-
self.logger.error "Exception raised while doing work!"
|
103
|
-
self.logger.error "#{exception.class}: #{exception.message}"
|
104
|
-
self.logger.error exception.backtrace.join("\n")
|
112
|
+
def despawn_worker(worker)
|
113
|
+
@mutex.synchronize do
|
114
|
+
@spawned -= 1
|
115
|
+
@workers.delete worker
|
105
116
|
end
|
106
117
|
end
|
107
118
|
|
108
|
-
def
|
109
|
-
@
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
@spawned < @max_workers
|
115
|
-
end
|
119
|
+
def do_work(work_item)
|
120
|
+
@do_work_proc.call(work_item)
|
121
|
+
rescue Exception => exception
|
122
|
+
self.logger.error "Exception raised while doing work!"
|
123
|
+
self.logger.error "#{exception.class}: #{exception.message}"
|
124
|
+
self.logger.error exception.backtrace.join("\n")
|
116
125
|
end
|
117
126
|
|
118
127
|
class WorkersWaiting
|
@@ -133,6 +142,26 @@ class DatWorkerPool
|
|
133
142
|
|
134
143
|
end
|
135
144
|
|
145
|
+
class OptionalTimeoutProc
|
146
|
+
def initialize(timeout, reraise = false, &proc)
|
147
|
+
@timeout = timeout
|
148
|
+
@reraise = reraise
|
149
|
+
@proc = proc
|
150
|
+
end
|
151
|
+
|
152
|
+
def call
|
153
|
+
if @timeout
|
154
|
+
begin
|
155
|
+
SystemTimer.timeout(@timeout, TimeoutError, &@proc)
|
156
|
+
rescue TimeoutError
|
157
|
+
raise if @reraise
|
158
|
+
end
|
159
|
+
else
|
160
|
+
@proc.call
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
136
165
|
module Logger
|
137
166
|
def self.new(debug)
|
138
167
|
debug ? ::Logger.new(STDOUT) : ::Logger.new(File.open('/dev/null', 'w'))
|
@@ -26,19 +26,17 @@ class DatWorkerPool
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def pop
|
29
|
-
|
29
|
+
return if @shutdown
|
30
|
+
@mutex.synchronize do
|
31
|
+
@condition_variable.wait(@mutex) while !@shutdown && @work_items.empty?
|
32
|
+
@work_items.shift
|
33
|
+
end
|
30
34
|
end
|
31
35
|
|
32
36
|
def empty?
|
33
37
|
@mutex.synchronize{ @work_items.empty? }
|
34
38
|
end
|
35
39
|
|
36
|
-
# wait to be signaled by `push`
|
37
|
-
def wait_for_work_item
|
38
|
-
return if @shutdown
|
39
|
-
@mutex.synchronize{ @condition_variable.wait(@mutex) }
|
40
|
-
end
|
41
|
-
|
42
40
|
# wake up any workers who are idle (because of `wait_for_work_item`)
|
43
41
|
def shutdown
|
44
42
|
@shutdown = true
|
@@ -3,14 +3,25 @@ require 'thread'
|
|
3
3
|
class DatWorkerPool
|
4
4
|
|
5
5
|
class Worker
|
6
|
+
attr_writer :on_work, :on_waiting, :on_continuing, :on_shutdown
|
6
7
|
|
7
|
-
def initialize(
|
8
|
-
@
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@
|
12
|
-
@
|
13
|
-
|
8
|
+
def initialize(queue)
|
9
|
+
@queue = queue
|
10
|
+
@on_work = proc{ |work_item| }
|
11
|
+
@on_waiting = proc{ |worker| }
|
12
|
+
@on_continuing = proc{ |worker| }
|
13
|
+
@on_shutdown = proc{ |worker| }
|
14
|
+
|
15
|
+
@shutdown = false
|
16
|
+
@thread = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def start
|
20
|
+
@thread ||= Thread.new{ work_loop }
|
21
|
+
end
|
22
|
+
|
23
|
+
def running?
|
24
|
+
@thread && @thread.alive?
|
14
25
|
end
|
15
26
|
|
16
27
|
def shutdown
|
@@ -18,29 +29,22 @@ class DatWorkerPool
|
|
18
29
|
end
|
19
30
|
|
20
31
|
def join(*args)
|
21
|
-
@thread.join(*args) if
|
32
|
+
@thread.join(*args) if running?
|
22
33
|
end
|
23
34
|
|
24
35
|
protected
|
25
36
|
|
26
37
|
def work_loop
|
27
38
|
loop do
|
28
|
-
self
|
39
|
+
@on_waiting.call(self)
|
40
|
+
work_item = @queue.pop
|
41
|
+
@on_continuing.call(self)
|
29
42
|
break if @shutdown
|
30
|
-
@
|
43
|
+
@on_work.call(work_item) if work_item
|
31
44
|
end
|
32
45
|
ensure
|
33
|
-
@
|
34
|
-
|
35
|
-
|
36
|
-
# Wait for work to process by checking if the queue is empty.
|
37
|
-
def wait_for_work
|
38
|
-
while @queue.empty?
|
39
|
-
return if @shutdown
|
40
|
-
@workers_waiting.increment
|
41
|
-
@queue.wait_for_work_item
|
42
|
-
@workers_waiting.decrement
|
43
|
-
end
|
46
|
+
@on_shutdown.call(self)
|
47
|
+
@thread = nil
|
44
48
|
end
|
45
49
|
|
46
50
|
end
|
@@ -3,7 +3,7 @@ require 'dat-worker-pool'
|
|
3
3
|
|
4
4
|
class DatWorkerPool
|
5
5
|
|
6
|
-
class
|
6
|
+
class UnitTests < Assert::Context
|
7
7
|
desc "DatWorkerPool"
|
8
8
|
setup do
|
9
9
|
@work_pool = DatWorkerPool.new{ }
|
@@ -13,34 +13,49 @@ class DatWorkerPool
|
|
13
13
|
should have_readers :logger, :spawned
|
14
14
|
should have_imeths :add_work, :shutdown, :despawn_worker
|
15
15
|
should have_imeths :work_items, :waiting
|
16
|
+
should have_imeths :worker_available?, :all_spawned_workers_are_busy?
|
17
|
+
should have_imeths :reached_max_workers?
|
18
|
+
should have_imeths :queue_empty?
|
16
19
|
|
17
20
|
end
|
18
21
|
|
19
|
-
class WorkerBehaviorTests <
|
22
|
+
class WorkerBehaviorTests < UnitTests
|
20
23
|
desc "workers"
|
21
24
|
setup do
|
22
|
-
@work_pool = DatWorkerPool.new(1, 2, true){|work| sleep(work) }
|
25
|
+
@work_pool = DatWorkerPool.new(1, 2, true){ |work| sleep(work) }
|
23
26
|
end
|
24
27
|
|
25
28
|
should "be created as needed and only go up to the maximum number allowed" do
|
26
29
|
# the minimum should be spawned and waiting
|
27
30
|
assert_equal 1, @work_pool.spawned
|
28
31
|
assert_equal 1, @work_pool.waiting
|
32
|
+
assert_equal true, @work_pool.worker_available?
|
33
|
+
assert_equal false, @work_pool.all_spawned_workers_are_busy?
|
34
|
+
assert_equal false, @work_pool.reached_max_workers?
|
29
35
|
|
30
36
|
# the minimum should be spawned, but no longer waiting
|
31
37
|
@work_pool.add_work 5
|
32
38
|
assert_equal 1, @work_pool.spawned
|
33
39
|
assert_equal 0, @work_pool.waiting
|
40
|
+
assert_equal true, @work_pool.worker_available?
|
41
|
+
assert_equal true, @work_pool.all_spawned_workers_are_busy?
|
42
|
+
assert_equal false, @work_pool.reached_max_workers?
|
34
43
|
|
35
44
|
# an additional worker should be spawned
|
36
45
|
@work_pool.add_work 5
|
37
46
|
assert_equal 2, @work_pool.spawned
|
38
47
|
assert_equal 0, @work_pool.waiting
|
48
|
+
assert_equal false, @work_pool.worker_available?
|
49
|
+
assert_equal true, @work_pool.all_spawned_workers_are_busy?
|
50
|
+
assert_equal true, @work_pool.reached_max_workers?
|
39
51
|
|
40
52
|
# no additional workers are spawned, the work waits to be processed
|
41
53
|
@work_pool.add_work 5
|
42
54
|
assert_equal 2, @work_pool.spawned
|
43
55
|
assert_equal 0, @work_pool.waiting
|
56
|
+
assert_equal false, @work_pool.worker_available?
|
57
|
+
assert_equal true, @work_pool.all_spawned_workers_are_busy?
|
58
|
+
assert_equal true, @work_pool.reached_max_workers?
|
44
59
|
end
|
45
60
|
|
46
61
|
should "go back to waiting when they finish working" do
|
@@ -59,7 +74,20 @@ class DatWorkerPool
|
|
59
74
|
|
60
75
|
end
|
61
76
|
|
62
|
-
class
|
77
|
+
class AddWorkWithNoWorkersTests < UnitTests
|
78
|
+
setup do
|
79
|
+
@work_pool = DatWorkerPool.new(0, 0){ |work| }
|
80
|
+
end
|
81
|
+
|
82
|
+
should "return whether or not the queue is empty" do
|
83
|
+
assert_equal true, @work_pool.queue_empty?
|
84
|
+
@work_pool.add_work 'test'
|
85
|
+
assert_equal false, @work_pool.queue_empty?
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
class AddWorkAndProcessItTests < UnitTests
|
63
91
|
desc "add_work and process"
|
64
92
|
setup do
|
65
93
|
@result = nil
|
@@ -84,7 +112,7 @@ class DatWorkerPool
|
|
84
112
|
|
85
113
|
end
|
86
114
|
|
87
|
-
class ShutdownTests <
|
115
|
+
class ShutdownTests < UnitTests
|
88
116
|
desc "shutdown"
|
89
117
|
setup do
|
90
118
|
@mutex = Mutex.new
|
@@ -101,7 +129,6 @@ class DatWorkerPool
|
|
101
129
|
should "allow any work that has been picked up to be processed" do
|
102
130
|
# make sure the workers haven't processed any work
|
103
131
|
assert_equal [], @finished
|
104
|
-
|
105
132
|
subject.shutdown(5)
|
106
133
|
|
107
134
|
# NOTE, the last work shouldn't have been processed, as it wasn't
|
@@ -123,6 +150,13 @@ class DatWorkerPool
|
|
123
150
|
end
|
124
151
|
end
|
125
152
|
|
153
|
+
should "allow jobs to finish by not providing a shutdown timeout" do
|
154
|
+
assert_equal [], @finished
|
155
|
+
subject.shutdown
|
156
|
+
assert_includes 'a', @finished
|
157
|
+
assert_includes 'b', @finished
|
158
|
+
end
|
159
|
+
|
126
160
|
end
|
127
161
|
|
128
162
|
end
|
data/test/unit/queue_tests.rb
CHANGED
@@ -3,7 +3,7 @@ require 'dat-worker-pool/queue'
|
|
3
3
|
|
4
4
|
class DatWorkerPool::Queue
|
5
5
|
|
6
|
-
class
|
6
|
+
class UnitTests < Assert::Context
|
7
7
|
desc "DatWorkerPool::Queue"
|
8
8
|
setup do
|
9
9
|
@queue = DatWorkerPool::Queue.new
|
@@ -11,7 +11,7 @@ class DatWorkerPool::Queue
|
|
11
11
|
subject{ @queue }
|
12
12
|
|
13
13
|
should have_imeths :work_items, :push, :pop, :empty?
|
14
|
-
should have_imeths :
|
14
|
+
should have_imeths :shutdown
|
15
15
|
|
16
16
|
should "allow pushing work items onto the queue with #push" do
|
17
17
|
subject.push 'work'
|
@@ -34,6 +34,12 @@ class DatWorkerPool::Queue
|
|
34
34
|
assert_equal 0, subject.work_items.size
|
35
35
|
end
|
36
36
|
|
37
|
+
should "return nothing with pop when the queue has been shutdown" do
|
38
|
+
subject.push 'work1'
|
39
|
+
subject.shutdown
|
40
|
+
assert_nil subject.pop
|
41
|
+
end
|
42
|
+
|
37
43
|
should "return whether the queue is empty or not with #empty?" do
|
38
44
|
assert subject.empty?
|
39
45
|
subject.push 'work'
|
@@ -42,25 +48,32 @@ class DatWorkerPool::Queue
|
|
42
48
|
assert subject.empty?
|
43
49
|
end
|
44
50
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
51
|
+
end
|
52
|
+
|
53
|
+
class SignallingTests < UnitTests
|
54
|
+
desc "mutex and condition variable behavior"
|
55
|
+
setup do
|
56
|
+
@thread = Thread.new do
|
57
|
+
Thread.current['work_item'] = @queue.pop || 'got nothing'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
should "have threads wait for a work item to be added when using pop" do
|
62
|
+
assert_equal "sleep", @thread.status
|
63
|
+
end
|
64
|
+
|
65
|
+
should "wakeup threads when work is pushed onto the queue" do
|
66
|
+
subject.push 'some work'
|
67
|
+
sleep 0.1
|
68
|
+
assert !@thread.alive?
|
69
|
+
assert_equal 'some work', @thread['work_item']
|
52
70
|
end
|
53
71
|
|
54
|
-
should "
|
55
|
-
"wake them all up with #shutdown" do
|
56
|
-
thread1 = Thread.new{ subject.wait_for_work_item }
|
57
|
-
thread1.join(0.1) # ensure the thread runs enough to start waiting
|
58
|
-
thread2 = Thread.new{ subject.wait_for_work_item }
|
59
|
-
thread2.join(0.1) # ensure the thread runs enough to start waiting
|
72
|
+
should "wakeup thread when the queue is shutdown" do
|
60
73
|
subject.shutdown
|
61
|
-
|
62
|
-
|
63
|
-
|
74
|
+
sleep 0.1
|
75
|
+
assert !@thread.alive?
|
76
|
+
assert_equal 'got nothing', @thread['work_item']
|
64
77
|
end
|
65
78
|
|
66
79
|
end
|
data/test/unit/worker_tests.rb
CHANGED
@@ -6,24 +6,108 @@ require 'dat-worker-pool/queue'
|
|
6
6
|
|
7
7
|
class DatWorkerPool::Worker
|
8
8
|
|
9
|
-
class
|
9
|
+
class UnitTests < Assert::Context
|
10
10
|
desc "DatWorkerPool::Worker"
|
11
11
|
setup do
|
12
|
-
@pool = DatWorkerPool.new{ }
|
13
12
|
@queue = DatWorkerPool::Queue.new
|
14
|
-
@
|
15
|
-
@worker = DatWorkerPool::Worker.new(@
|
13
|
+
@work_done = []
|
14
|
+
@worker = DatWorkerPool::Worker.new(@queue).tap do |w|
|
15
|
+
w.on_work = proc{ |work| @work_done << work }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
teardown do
|
19
|
+
@worker.shutdown
|
20
|
+
@queue.shutdown
|
21
|
+
@worker.join
|
16
22
|
end
|
17
23
|
subject{ @worker }
|
18
24
|
|
19
|
-
should
|
25
|
+
should have_writers :on_waiting, :on_continuing, :on_shutdown
|
26
|
+
should have_imeths :start, :shutdown, :join, :running?
|
27
|
+
|
28
|
+
should "start a thread with it's work loop with #start" do
|
29
|
+
thread = nil
|
30
|
+
assert_nothing_raised{ thread = subject.start }
|
31
|
+
assert_instance_of Thread, thread
|
32
|
+
assert thread.alive?
|
33
|
+
assert subject.running?
|
34
|
+
end
|
35
|
+
|
36
|
+
should "call the block it's passed when it get's work from the queue" do
|
37
|
+
subject.start
|
38
|
+
@queue.push 'one'
|
39
|
+
subject.join 0.1 # trigger the worker's thread to run
|
40
|
+
@queue.push 'two'
|
41
|
+
subject.join 0.1 # trigger the worker's thread to run
|
42
|
+
assert_equal [ 'one', 'two' ], @work_done
|
43
|
+
end
|
44
|
+
|
45
|
+
should "flag itself for exiting it's work loop with #shutdown and " \
|
46
|
+
"end it's thread once it's queue is shutdown" do
|
47
|
+
thread = subject.start
|
48
|
+
subject.join 0.1 # trigger the worker's thread to run, allow it to get into it's
|
49
|
+
# work loop
|
50
|
+
assert_nothing_raised{ subject.shutdown }
|
51
|
+
@queue.shutdown
|
52
|
+
|
53
|
+
subject.join 0.1 # trigger the worker's thread to run, should exit
|
54
|
+
assert_not thread.alive?
|
55
|
+
assert_not subject.running?
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
class CallbacksTests < UnitTests
|
61
|
+
desc "callbacks"
|
62
|
+
setup do
|
63
|
+
@worker = DatWorkerPool::Worker.new(@queue).tap do |w|
|
64
|
+
w.on_work = proc{ |work| sleep 0.2 }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
should "call the on waiting callback, yielding itself, when " \
|
69
|
+
"it's waiting on work from the queue" do
|
70
|
+
waiting, yielded_worker = nil, nil
|
71
|
+
subject.on_waiting = proc do |worker|
|
72
|
+
waiting = true
|
73
|
+
yielded_worker = worker
|
74
|
+
end
|
75
|
+
subject.start
|
76
|
+
subject.join 0.1 # trigger the worker's thread to run
|
77
|
+
|
78
|
+
assert_equal true, waiting
|
79
|
+
assert_equal subject, yielded_worker
|
80
|
+
end
|
81
|
+
|
82
|
+
should "call the on continuing callback, yielding itself, when " \
|
83
|
+
"it's done waiting for work from the queue" do
|
84
|
+
waiting, yielded_worker = nil, nil
|
85
|
+
subject.on_continuing = proc do |worker|
|
86
|
+
waiting = false
|
87
|
+
yielded_worker = worker
|
88
|
+
end
|
89
|
+
subject.start
|
90
|
+
@queue.push 'some work'
|
91
|
+
subject.join 0.1 # trigger the worker's thread to run
|
20
92
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
93
|
+
assert_equal false, waiting
|
94
|
+
assert_equal subject, yielded_worker
|
95
|
+
end
|
96
|
+
|
97
|
+
should "call the on shutdown callback, yielding itself, when " \
|
98
|
+
"it's shutdown" do
|
99
|
+
shutdown, yielded_worker = nil, nil
|
100
|
+
subject.on_shutdown = proc do |worker|
|
101
|
+
shutdown = true
|
102
|
+
yielded_worker = worker
|
103
|
+
end
|
104
|
+
subject.start
|
25
105
|
subject.shutdown
|
26
|
-
|
106
|
+
@queue.shutdown
|
107
|
+
subject.join 0.1 # trigger the worker's thread to run
|
108
|
+
|
109
|
+
assert_equal true, shutdown
|
110
|
+
assert_equal subject, yielded_worker
|
27
111
|
end
|
28
112
|
|
29
113
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dat-worker-pool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Collin Redding
|
@@ -16,10 +16,9 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2013-
|
19
|
+
date: 2013-10-01 00:00:00 Z
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
|
-
prerelease: false
|
23
22
|
version_requirements: &id001 !ruby/object:Gem::Requirement
|
24
23
|
none: false
|
25
24
|
requirements:
|
@@ -30,11 +29,11 @@ dependencies:
|
|
30
29
|
- 1
|
31
30
|
- 2
|
32
31
|
version: "1.2"
|
32
|
+
type: :runtime
|
33
33
|
requirement: *id001
|
34
|
+
prerelease: false
|
34
35
|
name: SystemTimer
|
35
|
-
type: :runtime
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
|
-
prerelease: false
|
38
37
|
version_requirements: &id002 !ruby/object:Gem::Requirement
|
39
38
|
none: false
|
40
39
|
requirements:
|
@@ -44,9 +43,10 @@ dependencies:
|
|
44
43
|
segments:
|
45
44
|
- 0
|
46
45
|
version: "0"
|
46
|
+
type: :development
|
47
47
|
requirement: *id002
|
48
|
+
prerelease: false
|
48
49
|
name: assert
|
49
|
-
type: :development
|
50
50
|
description: A simple thread pool for processing generic 'work'
|
51
51
|
email:
|
52
52
|
- collin.redding@me.com
|
@@ -76,8 +76,8 @@ files:
|
|
76
76
|
- test/unit/worker_tests.rb
|
77
77
|
- tmp/.gitkeep
|
78
78
|
homepage: http://github.com/redding/dat-worker-pool
|
79
|
-
licenses:
|
80
|
-
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
81
|
post_install_message:
|
82
82
|
rdoc_options: []
|
83
83
|
|