dat-worker-pool 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|