dat-worker-pool 0.3.0 → 0.4.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 -1
- data/lib/dat-worker-pool.rb +45 -7
- data/lib/dat-worker-pool/queue.rb +17 -1
- data/lib/dat-worker-pool/version.rb +1 -1
- data/lib/dat-worker-pool/worker.rb +23 -9
- data/lib/dat-worker-pool/worker_pool_spy.rb +47 -4
- data/test/system/use_worker_pool_tests.rb +1 -0
- data/test/unit/dat-worker-pool_tests.rb +112 -2
- data/test/unit/queue_tests.rb +30 -1
- data/test/unit/worker_pool_spy_tests.rb +75 -5
- data/test/unit/worker_tests.rb +76 -35
- metadata +16 -15
data/dat-worker-pool.gemspec
CHANGED
data/lib/dat-worker-pool.rb
CHANGED
@@ -11,6 +11,10 @@ class DatWorkerPool
|
|
11
11
|
TimeoutError = Class.new(RuntimeError)
|
12
12
|
|
13
13
|
attr_reader :logger, :spawned
|
14
|
+
attr_reader :queue
|
15
|
+
attr_reader :on_worker_start_callbacks, :on_worker_shutdown_callbacks
|
16
|
+
attr_reader :on_worker_sleep_callbacks, :on_worker_wakeup_callbacks
|
17
|
+
attr_reader :before_work_callbacks, :after_work_callbacks
|
14
18
|
|
15
19
|
def initialize(min = 0, max = 1, debug = false, &do_work_proc)
|
16
20
|
@min_workers = min
|
@@ -26,7 +30,14 @@ class DatWorkerPool
|
|
26
30
|
@workers = []
|
27
31
|
@spawned = 0
|
28
32
|
|
29
|
-
@
|
33
|
+
@on_worker_start_callbacks = []
|
34
|
+
@on_worker_shutdown_callbacks = [proc{ |worker| despawn_worker(worker) }]
|
35
|
+
@on_worker_sleep_callbacks = [proc{ @workers_waiting.increment }]
|
36
|
+
@on_worker_wakeup_callbacks = [proc{ @workers_waiting.decrement }]
|
37
|
+
@before_work_callbacks = []
|
38
|
+
@after_work_callbacks = []
|
39
|
+
|
40
|
+
@started = false
|
30
41
|
end
|
31
42
|
|
32
43
|
def work_items
|
@@ -62,7 +73,13 @@ class DatWorkerPool
|
|
62
73
|
return if work_item.nil?
|
63
74
|
new_worker_needed = all_spawned_workers_are_busy?
|
64
75
|
@queue.push work_item
|
65
|
-
self.spawn_worker if new_worker_needed && !reached_max_workers?
|
76
|
+
self.spawn_worker if @started && new_worker_needed && !reached_max_workers?
|
77
|
+
end
|
78
|
+
|
79
|
+
def start
|
80
|
+
@started = true
|
81
|
+
@queue.start
|
82
|
+
@min_workers.times{ self.spawn_worker }
|
66
83
|
end
|
67
84
|
|
68
85
|
# Shutdown each worker and then the queue. Shutting down the queue will
|
@@ -72,8 +89,13 @@ class DatWorkerPool
|
|
72
89
|
# **NOTE** Any work that is left on the queue isn't processed. The controlling
|
73
90
|
# application for the worker pool should gracefully handle these items.
|
74
91
|
def shutdown(timeout = nil)
|
92
|
+
@started = false
|
75
93
|
begin
|
76
94
|
proc = OptionalTimeoutProc.new(timeout, true) do
|
95
|
+
# Workers need to be shutdown before the queue. This marks a flag that
|
96
|
+
# tells the workers to exit out of their loop once they wakeup. The
|
97
|
+
# queue shutdown signals the workers to wakeup, so the flag needs to be
|
98
|
+
# set before they wakeup.
|
77
99
|
@workers.each(&:shutdown)
|
78
100
|
@queue.shutdown
|
79
101
|
|
@@ -91,15 +113,32 @@ class DatWorkerPool
|
|
91
113
|
end
|
92
114
|
end
|
93
115
|
|
116
|
+
def on_queue_pop_callbacks; @queue.on_pop_callbacks; end
|
117
|
+
def on_queue_push_callbacks; @queue.on_push_callbacks; end
|
118
|
+
|
119
|
+
def on_queue_pop(&block); @queue.on_pop_callbacks << block; end
|
120
|
+
def on_queue_push(&block); @queue.on_push_callbacks << block; end
|
121
|
+
|
122
|
+
def on_worker_start(&block); @on_worker_start_callbacks << block; end
|
123
|
+
def on_worker_shutdown(&block); @on_worker_shutdown_callbacks << block; end
|
124
|
+
def on_worker_sleep(&block); @on_worker_sleep_callbacks << block; end
|
125
|
+
def on_worker_wakeup(&block); @on_worker_wakeup_callbacks << block; end
|
126
|
+
|
127
|
+
def before_work(&block); @before_work_callbacks << block; end
|
128
|
+
def after_work(&block); @after_work_callbacks << block; end
|
129
|
+
|
94
130
|
protected
|
95
131
|
|
96
132
|
def spawn_worker
|
97
133
|
@mutex.synchronize do
|
98
134
|
Worker.new(@queue).tap do |w|
|
99
|
-
w.on_work
|
100
|
-
w.
|
101
|
-
w.
|
102
|
-
w.
|
135
|
+
w.on_work = proc{ |worker, work_item| do_work(work_item) }
|
136
|
+
w.on_start_callbacks = @on_worker_start_callbacks
|
137
|
+
w.on_shutdown_callbacks = @on_worker_shutdown_callbacks
|
138
|
+
w.on_sleep_callbacks = @on_worker_sleep_callbacks
|
139
|
+
w.on_wakeup_callbacks = @on_worker_wakeup_callbacks
|
140
|
+
w.before_work_callbacks = @before_work_callbacks
|
141
|
+
w.after_work_callbacks = @after_work_callbacks
|
103
142
|
|
104
143
|
@workers << w
|
105
144
|
@spawned += 1
|
@@ -139,7 +178,6 @@ class DatWorkerPool
|
|
139
178
|
def decrement
|
140
179
|
@mutex.synchronize{ @count -= 1 }
|
141
180
|
end
|
142
|
-
|
143
181
|
end
|
144
182
|
|
145
183
|
class OptionalTimeoutProc
|
@@ -4,11 +4,16 @@ class DatWorkerPool
|
|
4
4
|
|
5
5
|
class Queue
|
6
6
|
|
7
|
+
attr_accessor :on_push_callbacks, :on_pop_callbacks
|
8
|
+
|
7
9
|
def initialize
|
8
10
|
@work_items = []
|
9
11
|
@shutdown = false
|
10
12
|
@mutex = Mutex.new
|
11
13
|
@condition_variable = ConditionVariable.new
|
14
|
+
|
15
|
+
@on_pop_callbacks = []
|
16
|
+
@on_push_callbacks = []
|
12
17
|
end
|
13
18
|
|
14
19
|
def work_items
|
@@ -23,26 +28,37 @@ class DatWorkerPool
|
|
23
28
|
@work_items << work_item
|
24
29
|
@condition_variable.signal
|
25
30
|
end
|
31
|
+
@on_push_callbacks.each(&:call)
|
26
32
|
end
|
27
33
|
|
28
34
|
def pop
|
29
35
|
return if @shutdown
|
30
|
-
@mutex.synchronize do
|
36
|
+
item = @mutex.synchronize do
|
31
37
|
@condition_variable.wait(@mutex) while !@shutdown && @work_items.empty?
|
32
38
|
@work_items.shift
|
33
39
|
end
|
40
|
+
@on_pop_callbacks.each(&:call)
|
41
|
+
item
|
34
42
|
end
|
35
43
|
|
36
44
|
def empty?
|
37
45
|
@mutex.synchronize{ @work_items.empty? }
|
38
46
|
end
|
39
47
|
|
48
|
+
def start
|
49
|
+
@shutdown = false
|
50
|
+
end
|
51
|
+
|
40
52
|
# wake up any workers who are idle (because of `wait_for_work_item`)
|
41
53
|
def shutdown
|
42
54
|
@shutdown = true
|
43
55
|
@mutex.synchronize{ @condition_variable.broadcast }
|
44
56
|
end
|
45
57
|
|
58
|
+
def shutdown?
|
59
|
+
@shutdown
|
60
|
+
end
|
61
|
+
|
46
62
|
end
|
47
63
|
|
48
64
|
end
|
@@ -3,14 +3,21 @@ require 'thread'
|
|
3
3
|
class DatWorkerPool
|
4
4
|
|
5
5
|
class Worker
|
6
|
-
|
6
|
+
|
7
|
+
attr_accessor :on_work
|
8
|
+
attr_accessor :on_start_callbacks, :on_shutdown_callbacks
|
9
|
+
attr_accessor :on_sleep_callbacks, :on_wakeup_callbacks
|
10
|
+
attr_accessor :before_work_callbacks, :after_work_callbacks
|
7
11
|
|
8
12
|
def initialize(queue)
|
9
13
|
@queue = queue
|
10
|
-
@on_work
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@
|
14
|
+
@on_work = proc{ |worker, work_item| }
|
15
|
+
@on_start_callbacks = []
|
16
|
+
@on_shutdown_callbacks = []
|
17
|
+
@on_sleep_callbacks = []
|
18
|
+
@on_wakeup_callbacks = []
|
19
|
+
@before_work_callbacks = []
|
20
|
+
@after_work_callbacks = []
|
14
21
|
|
15
22
|
@shutdown = false
|
16
23
|
@thread = nil
|
@@ -35,18 +42,25 @@ class DatWorkerPool
|
|
35
42
|
protected
|
36
43
|
|
37
44
|
def work_loop
|
45
|
+
@on_start_callbacks.each{ |p| p.call(self) }
|
38
46
|
loop do
|
39
|
-
@
|
47
|
+
@on_sleep_callbacks.each{ |p| p.call(self) }
|
40
48
|
work_item = @queue.pop
|
41
|
-
@
|
49
|
+
@on_wakeup_callbacks.each{ |p| p.call(self) }
|
42
50
|
break if @shutdown
|
43
|
-
|
51
|
+
do_work(work_item) if work_item
|
44
52
|
end
|
45
53
|
ensure
|
46
|
-
@
|
54
|
+
@on_shutdown_callbacks.each{ |p| p.call(self) }
|
47
55
|
@thread = nil
|
48
56
|
end
|
49
57
|
|
58
|
+
def do_work(work_item)
|
59
|
+
@before_work_callbacks.each{ |p| p.call(self, work_item) }
|
60
|
+
@on_work.call(self, work_item)
|
61
|
+
@after_work_callbacks.each{ |p| p.call(self, work_item) }
|
62
|
+
end
|
63
|
+
|
50
64
|
end
|
51
65
|
|
52
66
|
end
|
@@ -1,15 +1,36 @@
|
|
1
1
|
class DatWorkerPool
|
2
2
|
|
3
3
|
class WorkerPoolSpy
|
4
|
-
attr_reader :
|
4
|
+
attr_reader :min_workers, :max_workers, :debug
|
5
|
+
attr_reader :work_proc, :work_items
|
6
|
+
attr_reader :start_called
|
5
7
|
attr_reader :shutdown_called, :shutdown_timeout
|
8
|
+
attr_reader :on_queue_pop_callbacks, :on_queue_push_callbacks
|
9
|
+
attr_reader :on_worker_start_callbacks, :on_worker_shutdown_callbacks
|
10
|
+
attr_reader :on_worker_sleep_callbacks, :on_worker_wakeup_callbacks
|
11
|
+
attr_reader :before_work_callbacks, :after_work_callbacks
|
6
12
|
attr_accessor :worker_available
|
7
13
|
|
8
|
-
def initialize
|
14
|
+
def initialize(min = 0, max = 1, debug = false, &block)
|
15
|
+
@min_workers = min
|
16
|
+
@max_workers = max
|
17
|
+
@debug = debug
|
18
|
+
@work_proc = block
|
19
|
+
|
9
20
|
@worker_available = false
|
10
21
|
@work_items = []
|
22
|
+
@start_called = false
|
11
23
|
@shutdown_called = false
|
12
24
|
@shutdown_timeout = nil
|
25
|
+
|
26
|
+
@on_queue_pop_callbacks = []
|
27
|
+
@on_queue_push_callbacks = []
|
28
|
+
@on_worker_start_callbacks = []
|
29
|
+
@on_worker_shutdown_callbacks = []
|
30
|
+
@on_worker_sleep_callbacks = []
|
31
|
+
@on_worker_wakeup_callbacks = []
|
32
|
+
@before_work_callbacks = []
|
33
|
+
@after_work_callbacks = []
|
13
34
|
end
|
14
35
|
|
15
36
|
def worker_available?
|
@@ -21,13 +42,35 @@ class DatWorkerPool
|
|
21
42
|
end
|
22
43
|
|
23
44
|
def add_work(work)
|
24
|
-
|
45
|
+
return unless work
|
46
|
+
@work_items << work
|
47
|
+
@on_queue_push_callbacks.each(&:call)
|
48
|
+
end
|
49
|
+
|
50
|
+
def pop_work
|
51
|
+
work = @work_items.shift
|
52
|
+
@on_queue_pop_callbacks.each(&:call)
|
53
|
+
work
|
25
54
|
end
|
26
55
|
|
27
|
-
def
|
56
|
+
def start
|
57
|
+
@start_called = true
|
58
|
+
end
|
59
|
+
|
60
|
+
def shutdown(timeout = nil)
|
28
61
|
@shutdown_called = true
|
29
62
|
@shutdown_timeout = timeout
|
30
63
|
end
|
64
|
+
|
65
|
+
def on_queue_pop(&block); @on_queue_pop_callbacks << block; end
|
66
|
+
def on_queue_push(&block); @on_queue_push_callbacks << block; end
|
67
|
+
def on_worker_start(&block); @on_worker_start_callbacks << block; end
|
68
|
+
def on_worker_shutdown(&block); @on_worker_shutdown_callbacks << block; end
|
69
|
+
def on_worker_sleep(&block); @on_worker_sleep_callbacks << block; end
|
70
|
+
def on_worker_wakeup(&block); @on_worker_wakeup_callbacks << block; end
|
71
|
+
def before_work(&block); @before_work_callbacks << block; end
|
72
|
+
def after_work(&block); @after_work_callbacks << block; end
|
73
|
+
|
31
74
|
end
|
32
75
|
|
33
76
|
end
|
@@ -10,6 +10,7 @@ class UseWorkerPoolTests < Assert::Context
|
|
10
10
|
@work_pool = DatWorkerPool.new(1, 2, !!ENV['DEBUG']) do |work|
|
11
11
|
@mutex.synchronize{ @results << (work * 100) }
|
12
12
|
end
|
13
|
+
@work_pool.start
|
13
14
|
end
|
14
15
|
|
15
16
|
should "be able to add work, have it processed and stop the pool" do
|
@@ -10,12 +10,47 @@ class DatWorkerPool
|
|
10
10
|
end
|
11
11
|
subject{ @work_pool }
|
12
12
|
|
13
|
-
should have_readers :logger, :spawned
|
14
|
-
should
|
13
|
+
should have_readers :logger, :spawned, :queue
|
14
|
+
should have_readers :on_worker_start_callbacks, :on_worker_shutdown_callbacks
|
15
|
+
should have_readers :on_worker_sleep_callbacks, :on_worker_wakeup_callbacks
|
16
|
+
should have_readers :before_work_callbacks, :after_work_callbacks
|
17
|
+
should have_imeths :add_work, :start, :shutdown, :despawn_worker
|
15
18
|
should have_imeths :work_items, :waiting
|
16
19
|
should have_imeths :worker_available?, :all_spawned_workers_are_busy?
|
17
20
|
should have_imeths :reached_max_workers?
|
18
21
|
should have_imeths :queue_empty?
|
22
|
+
should have_imeths :on_queue_pop_callbacks, :on_queue_push_callbacks
|
23
|
+
should have_imeths :on_queue_pop, :on_queue_push
|
24
|
+
should have_imeths :on_worker_start, :on_worker_shutdown
|
25
|
+
should have_imeths :on_worker_sleep, :on_worker_wakeup
|
26
|
+
should have_imeths :before_work, :after_work
|
27
|
+
|
28
|
+
should "know its attributes" do
|
29
|
+
assert_instance_of ::Logger, subject.logger
|
30
|
+
assert_equal 0, subject.spawned
|
31
|
+
assert_instance_of Queue, subject.queue
|
32
|
+
end
|
33
|
+
|
34
|
+
should "default its worker callbacks" do
|
35
|
+
assert_equal [], subject.on_worker_start_callbacks
|
36
|
+
assert_equal 1, subject.on_worker_shutdown_callbacks.size
|
37
|
+
assert_instance_of Proc, subject.on_worker_shutdown_callbacks.first
|
38
|
+
assert_equal 1, subject.on_worker_sleep_callbacks.size
|
39
|
+
assert_instance_of Proc, subject.on_worker_sleep_callbacks.first
|
40
|
+
assert_equal 1, subject.on_worker_wakeup_callbacks.size
|
41
|
+
assert_instance_of Proc, subject.on_worker_wakeup_callbacks.first
|
42
|
+
assert_equal [], subject.before_work_callbacks
|
43
|
+
assert_equal [], subject.after_work_callbacks
|
44
|
+
end
|
45
|
+
|
46
|
+
should "demeter its queue's callbacks" do
|
47
|
+
callback = proc{ }
|
48
|
+
subject.on_queue_pop(&callback)
|
49
|
+
assert_equal [callback], subject.on_queue_pop_callbacks
|
50
|
+
callback = proc{ }
|
51
|
+
subject.on_queue_push(&callback)
|
52
|
+
assert_equal [callback], subject.on_queue_push_callbacks
|
53
|
+
end
|
19
54
|
|
20
55
|
end
|
21
56
|
|
@@ -23,6 +58,7 @@ class DatWorkerPool
|
|
23
58
|
desc "workers"
|
24
59
|
setup do
|
25
60
|
@work_pool = DatWorkerPool.new(1, 2, true){ |work| sleep(work) }
|
61
|
+
@work_pool.start
|
26
62
|
end
|
27
63
|
|
28
64
|
should "be created as needed and only go up to the maximum number allowed" do
|
@@ -74,6 +110,51 @@ class DatWorkerPool
|
|
74
110
|
|
75
111
|
end
|
76
112
|
|
113
|
+
class WorkerCallbackTests < UnitTests
|
114
|
+
desc "worker callbacks"
|
115
|
+
setup do
|
116
|
+
@start_called = false
|
117
|
+
@shutdown_called = false
|
118
|
+
@sleep_called = false
|
119
|
+
@wakeup_called = false
|
120
|
+
@before_work_called = false
|
121
|
+
@after_work_called = false
|
122
|
+
|
123
|
+
@work_pool = DatWorkerPool.new(1){ |work| }.tap do |p|
|
124
|
+
p.on_worker_start{ @start_called = true }
|
125
|
+
p.on_worker_shutdown{ @shutdown_called = true }
|
126
|
+
p.on_worker_sleep{ @sleep_called = true }
|
127
|
+
p.on_worker_wakeup{ @wakeup_called = true }
|
128
|
+
p.before_work{ @before_work_called = true }
|
129
|
+
p.after_work{ @after_work_called = true }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
subject{ @work_pool }
|
133
|
+
|
134
|
+
should "call the worker callbacks as workers wait or wakeup" do
|
135
|
+
assert_false @start_called
|
136
|
+
assert_false @sleep_called
|
137
|
+
subject.start
|
138
|
+
assert_true @start_called
|
139
|
+
assert_true @sleep_called
|
140
|
+
assert_false @wakeup_called
|
141
|
+
assert_false @before_work_called
|
142
|
+
assert_false @after_work_called
|
143
|
+
@sleep_called = false
|
144
|
+
subject.add_work 'work'
|
145
|
+
assert_true @wakeup_called
|
146
|
+
assert_true @before_work_called
|
147
|
+
assert_true @after_work_called
|
148
|
+
assert_true @sleep_called
|
149
|
+
@wakeup_called = false
|
150
|
+
assert_false @shutdown_called
|
151
|
+
subject.shutdown
|
152
|
+
assert_true @wakeup_called
|
153
|
+
assert_true @shutdown_called
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
77
158
|
class AddWorkWithNoWorkersTests < UnitTests
|
78
159
|
setup do
|
79
160
|
@work_pool = DatWorkerPool.new(0, 0){ |work| }
|
@@ -92,6 +173,7 @@ class DatWorkerPool
|
|
92
173
|
setup do
|
93
174
|
@result = nil
|
94
175
|
@work_pool = DatWorkerPool.new(1){|work| @result = (2 / work) }
|
176
|
+
@work_pool.start
|
95
177
|
end
|
96
178
|
|
97
179
|
should "have added the work and processed it by calling the passed block" do
|
@@ -112,6 +194,33 @@ class DatWorkerPool
|
|
112
194
|
|
113
195
|
end
|
114
196
|
|
197
|
+
class StartTests < UnitTests
|
198
|
+
desc "start"
|
199
|
+
setup do
|
200
|
+
@work_pool = DatWorkerPool.new(1, 2){ |work| sleep(work) }
|
201
|
+
end
|
202
|
+
subject{ @work_pool }
|
203
|
+
|
204
|
+
should "start its queue" do
|
205
|
+
assert_false subject.queue.shutdown?
|
206
|
+
subject.start
|
207
|
+
assert_false subject.queue.shutdown?
|
208
|
+
subject.shutdown
|
209
|
+
assert_true subject.queue.shutdown?
|
210
|
+
subject.start
|
211
|
+
assert_false subject.queue.shutdown?
|
212
|
+
end
|
213
|
+
|
214
|
+
should "keep workers from being spawned until its called" do
|
215
|
+
assert_equal 0, subject.spawned
|
216
|
+
subject.add_work 1
|
217
|
+
assert_equal 0, subject.spawned
|
218
|
+
subject.start
|
219
|
+
assert_equal 1, subject.spawned
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
|
115
224
|
class ShutdownTests < UnitTests
|
116
225
|
desc "shutdown"
|
117
226
|
setup do
|
@@ -121,6 +230,7 @@ class DatWorkerPool
|
|
121
230
|
sleep 1
|
122
231
|
@mutex.synchronize{ @finished << work }
|
123
232
|
end
|
233
|
+
@work_pool.start
|
124
234
|
@work_pool.add_work 'a'
|
125
235
|
@work_pool.add_work 'b'
|
126
236
|
@work_pool.add_work 'c'
|
data/test/unit/queue_tests.rb
CHANGED
@@ -10,14 +10,27 @@ class DatWorkerPool::Queue
|
|
10
10
|
end
|
11
11
|
subject{ @queue }
|
12
12
|
|
13
|
+
should have_accessors :on_push_callbacks, :on_pop_callbacks
|
13
14
|
should have_imeths :work_items, :push, :pop, :empty?
|
14
|
-
should have_imeths :shutdown
|
15
|
+
should have_imeths :start, :shutdown, :shutdown?
|
16
|
+
|
17
|
+
should "default its callbacks" do
|
18
|
+
assert_equal [], subject.on_push_callbacks
|
19
|
+
assert_equal [], subject.on_pop_callbacks
|
20
|
+
end
|
15
21
|
|
16
22
|
should "allow pushing work items onto the queue with #push" do
|
17
23
|
subject.push 'work'
|
18
24
|
assert_equal [ 'work' ], subject.work_items
|
19
25
|
end
|
20
26
|
|
27
|
+
should "call its on push callback when work is pushed" do
|
28
|
+
on_push_called = false
|
29
|
+
subject.on_push_callbacks << proc{ on_push_called = true }
|
30
|
+
subject.push 'work'
|
31
|
+
assert_true on_push_called
|
32
|
+
end
|
33
|
+
|
21
34
|
should "raise an exception if trying to push work when shutdown" do
|
22
35
|
subject.shutdown
|
23
36
|
assert_raises(RuntimeError){ subject.push('work') }
|
@@ -34,6 +47,14 @@ class DatWorkerPool::Queue
|
|
34
47
|
assert_equal 0, subject.work_items.size
|
35
48
|
end
|
36
49
|
|
50
|
+
should "call its on pop callback when work is popped" do
|
51
|
+
subject.push 'work'
|
52
|
+
on_pop_called = false
|
53
|
+
subject.on_pop_callbacks << proc{ on_pop_called = true }
|
54
|
+
subject.pop
|
55
|
+
assert_true on_pop_called
|
56
|
+
end
|
57
|
+
|
37
58
|
should "return nothing with pop when the queue has been shutdown" do
|
38
59
|
subject.push 'work1'
|
39
60
|
subject.shutdown
|
@@ -48,6 +69,14 @@ class DatWorkerPool::Queue
|
|
48
69
|
assert subject.empty?
|
49
70
|
end
|
50
71
|
|
72
|
+
should "reset its shutdown flag when started" do
|
73
|
+
assert_false subject.shutdown?
|
74
|
+
subject.shutdown
|
75
|
+
assert_true subject.shutdown?
|
76
|
+
subject.start
|
77
|
+
assert_false subject.shutdown?
|
78
|
+
end
|
79
|
+
|
51
80
|
end
|
52
81
|
|
53
82
|
class SignallingTests < UnitTests
|
@@ -6,15 +6,28 @@ class DatWorkerPool::WorkerPoolSpy
|
|
6
6
|
class UnitTests < Assert::Context
|
7
7
|
desc "DatWorkerPool::WorkerPoolSpy"
|
8
8
|
setup do
|
9
|
-
@
|
9
|
+
@work_proc = proc{ 'work' }
|
10
|
+
@worker_pool_spy = DatWorkerPool::WorkerPoolSpy.new(&@work_proc)
|
10
11
|
end
|
11
12
|
subject{ @worker_pool_spy }
|
12
13
|
|
13
|
-
should have_readers :work_items
|
14
|
-
should have_readers :shutdown_called, :shutdown_timeout
|
14
|
+
should have_readers :work_proc, :work_items
|
15
|
+
should have_readers :start_called, :shutdown_called, :shutdown_timeout
|
16
|
+
should have_readers :on_queue_pop_callbacks, :on_queue_push_callbacks
|
17
|
+
should have_readers :on_worker_start_callbacks, :on_worker_shutdown_callbacks
|
18
|
+
should have_readers :on_worker_sleep_callbacks, :on_worker_wakeup_callbacks
|
19
|
+
should have_readers :before_work_callbacks, :after_work_callbacks
|
15
20
|
should have_accessors :worker_available
|
16
21
|
should have_imeths :worker_available?, :queue_empty?
|
17
|
-
should have_imeths :add_work, :shutdown
|
22
|
+
should have_imeths :add_work, :start, :shutdown
|
23
|
+
should have_imeths :on_queue_pop, :on_queue_push
|
24
|
+
should have_imeths :on_worker_start, :on_worker_shutdown
|
25
|
+
should have_imeths :on_worker_sleep, :on_worker_wakeup
|
26
|
+
should have_imeths :before_work, :after_work
|
27
|
+
|
28
|
+
should "know its work proc" do
|
29
|
+
assert_equal @work_proc, subject.work_proc
|
30
|
+
end
|
18
31
|
|
19
32
|
should "have nothing in it's work items by default" do
|
20
33
|
assert subject.work_items.empty?
|
@@ -25,6 +38,10 @@ class DatWorkerPool::WorkerPoolSpy
|
|
25
38
|
assert_not subject.worker_available?
|
26
39
|
end
|
27
40
|
|
41
|
+
should "return false for start called by default" do
|
42
|
+
assert_equal false, subject.start_called
|
43
|
+
end
|
44
|
+
|
28
45
|
should "return false for shutdown called by default" do
|
29
46
|
assert_equal false, subject.shutdown_called
|
30
47
|
end
|
@@ -56,12 +73,65 @@ class DatWorkerPool::WorkerPoolSpy
|
|
56
73
|
assert_equal false, subject.queue_empty?
|
57
74
|
end
|
58
75
|
|
76
|
+
should "know when it's been started" do
|
77
|
+
subject.start
|
78
|
+
assert_true subject.start_called
|
79
|
+
end
|
80
|
+
|
59
81
|
should "know when it's been shutdown and with what timeout" do
|
60
82
|
subject.shutdown(10)
|
61
|
-
|
83
|
+
assert_true subject.shutdown_called
|
62
84
|
assert_equal 10, subject.shutdown_timeout
|
63
85
|
end
|
64
86
|
|
87
|
+
should "allow calling shutdown with no timeout" do
|
88
|
+
subject.shutdown
|
89
|
+
assert_true subject.shutdown_called
|
90
|
+
assert_nil subject.shutdown_timeout
|
91
|
+
end
|
92
|
+
|
93
|
+
should "know its queue and worker callbacks" do
|
94
|
+
assert_equal [], subject.on_queue_pop_callbacks
|
95
|
+
callback = proc{ }
|
96
|
+
subject.on_queue_pop(&callback)
|
97
|
+
assert_equal [callback], subject.on_queue_pop_callbacks
|
98
|
+
|
99
|
+
assert_equal [], subject.on_queue_push_callbacks
|
100
|
+
callback = proc{ }
|
101
|
+
subject.on_queue_push(&callback)
|
102
|
+
assert_equal [callback], subject.on_queue_push_callbacks
|
103
|
+
|
104
|
+
assert_equal [], subject.on_worker_start_callbacks
|
105
|
+
callback = proc{ }
|
106
|
+
subject.on_worker_start(&callback)
|
107
|
+
assert_equal [callback], subject.on_worker_start_callbacks
|
108
|
+
|
109
|
+
assert_equal [], subject.on_worker_shutdown_callbacks
|
110
|
+
callback = proc{ }
|
111
|
+
subject.on_worker_shutdown(&callback)
|
112
|
+
assert_equal [callback], subject.on_worker_shutdown_callbacks
|
113
|
+
|
114
|
+
assert_equal [], subject.on_worker_sleep_callbacks
|
115
|
+
callback = proc{ }
|
116
|
+
subject.on_worker_sleep(&callback)
|
117
|
+
assert_equal [callback], subject.on_worker_sleep_callbacks
|
118
|
+
|
119
|
+
assert_equal [], subject.on_worker_wakeup_callbacks
|
120
|
+
callback = proc{ }
|
121
|
+
subject.on_worker_wakeup(&callback)
|
122
|
+
assert_equal [callback], subject.on_worker_wakeup_callbacks
|
123
|
+
|
124
|
+
assert_equal [], subject.before_work_callbacks
|
125
|
+
callback = proc{ }
|
126
|
+
subject.before_work(&callback)
|
127
|
+
assert_equal [callback], subject.before_work_callbacks
|
128
|
+
|
129
|
+
assert_equal [], subject.after_work_callbacks
|
130
|
+
callback = proc{ }
|
131
|
+
subject.after_work(&callback)
|
132
|
+
assert_equal [callback], subject.after_work_callbacks
|
133
|
+
end
|
134
|
+
|
65
135
|
end
|
66
136
|
|
67
137
|
end
|
data/test/unit/worker_tests.rb
CHANGED
@@ -12,7 +12,7 @@ class DatWorkerPool::Worker
|
|
12
12
|
@queue = DatWorkerPool::Queue.new
|
13
13
|
@work_done = []
|
14
14
|
@worker = DatWorkerPool::Worker.new(@queue).tap do |w|
|
15
|
-
w.on_work = proc{ |work| @work_done << work }
|
15
|
+
w.on_work = proc{ |worker, work| @work_done << work }
|
16
16
|
end
|
17
17
|
end
|
18
18
|
teardown do
|
@@ -22,9 +22,22 @@ class DatWorkerPool::Worker
|
|
22
22
|
end
|
23
23
|
subject{ @worker }
|
24
24
|
|
25
|
-
should
|
25
|
+
should have_accessors :on_work
|
26
|
+
should have_accessors :on_start_callbacks, :on_shutdown_callbacks
|
27
|
+
should have_accessors :on_sleep_callbacks, :on_wakeup_callbacks
|
28
|
+
should have_accessors :before_work_callbacks, :after_work_callbacks
|
26
29
|
should have_imeths :start, :shutdown, :join, :running?
|
27
30
|
|
31
|
+
should "default its callbacks" do
|
32
|
+
worker = DatWorkerPool::Worker.new(@queue)
|
33
|
+
assert_equal [], worker.on_start_callbacks
|
34
|
+
assert_equal [], worker.on_shutdown_callbacks
|
35
|
+
assert_equal [], worker.on_sleep_callbacks
|
36
|
+
assert_equal [], worker.on_wakeup_callbacks
|
37
|
+
assert_equal [], worker.before_work_callbacks
|
38
|
+
assert_equal [], worker.after_work_callbacks
|
39
|
+
end
|
40
|
+
|
28
41
|
should "start a thread with it's work loop with #start" do
|
29
42
|
thread = nil
|
30
43
|
assert_nothing_raised{ thread = subject.start }
|
@@ -60,54 +73,82 @@ class DatWorkerPool::Worker
|
|
60
73
|
class CallbacksTests < UnitTests
|
61
74
|
desc "callbacks"
|
62
75
|
setup do
|
76
|
+
@call_counter = 0
|
77
|
+
@on_start_called_with = nil
|
78
|
+
@on_start_call_count = nil
|
79
|
+
@on_shutdown_called_with = nil
|
80
|
+
@on_shutdown_call_count = nil
|
81
|
+
@on_sleep_called_with = nil
|
82
|
+
@on_sleep_call_count = nil
|
83
|
+
@on_wakeup_called_with = nil
|
84
|
+
@on_wakeup_call_count = nil
|
85
|
+
@before_work_called_with = nil
|
86
|
+
@before_work_called_at = nil
|
87
|
+
@after_work_called_with = nil
|
88
|
+
@after_work_called_at = nil
|
63
89
|
@worker = DatWorkerPool::Worker.new(@queue).tap do |w|
|
64
|
-
w.
|
90
|
+
w.on_start_callbacks << proc do |*args|
|
91
|
+
@on_start_called_with = args
|
92
|
+
@on_start_called_at = (@call_counter += 1)
|
93
|
+
end
|
94
|
+
w.on_shutdown_callbacks << proc do |*args|
|
95
|
+
@on_shutdown_called_with = args
|
96
|
+
@on_shutdown_called_at = (@call_counter += 1)
|
97
|
+
end
|
98
|
+
w.on_sleep_callbacks << proc do |*args|
|
99
|
+
@on_sleep_called_with = args
|
100
|
+
@on_sleep_called_at = (@call_counter += 1)
|
101
|
+
end
|
102
|
+
w.on_wakeup_callbacks << proc do |*args|
|
103
|
+
@on_wakeup_called_with = args
|
104
|
+
@on_wakeup_called_at = (@call_counter += 1)
|
105
|
+
end
|
106
|
+
w.before_work_callbacks << proc do |*args|
|
107
|
+
@before_work_called_with = args
|
108
|
+
@before_work_called_at = (@call_counter += 1)
|
109
|
+
end
|
110
|
+
w.after_work_callbacks << proc do |*args|
|
111
|
+
@after_work_called_with = args
|
112
|
+
@after_work_called_at = (@call_counter += 1)
|
113
|
+
end
|
65
114
|
end
|
66
115
|
end
|
67
116
|
|
68
|
-
should "
|
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
|
117
|
+
should "pass its self to its start, shutdown, sleep and wakupe callbacks" do
|
75
118
|
subject.start
|
76
|
-
|
119
|
+
@queue.push('work')
|
120
|
+
subject.shutdown
|
121
|
+
@queue.shutdown
|
77
122
|
|
78
|
-
assert_equal
|
79
|
-
assert_equal subject,
|
123
|
+
assert_equal [subject], @on_start_called_with
|
124
|
+
assert_equal [subject], @on_shutdown_called_with
|
125
|
+
assert_equal [subject], @on_sleep_called_with
|
126
|
+
assert_equal [subject], @on_wakeup_called_with
|
80
127
|
end
|
81
128
|
|
82
|
-
should "
|
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
|
129
|
+
should "pass its self and work to its beofre and after work callbacks" do
|
89
130
|
subject.start
|
90
|
-
@queue.push
|
91
|
-
subject.
|
131
|
+
@queue.push('work')
|
132
|
+
subject.shutdown
|
133
|
+
@queue.shutdown
|
92
134
|
|
93
|
-
assert_equal
|
94
|
-
assert_equal subject,
|
135
|
+
assert_equal [subject, 'work'], @before_work_called_with
|
136
|
+
assert_equal [subject, 'work'], @after_work_called_with
|
95
137
|
end
|
96
138
|
|
97
|
-
should "call
|
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
|
139
|
+
should "call its callbacks throughout its lifecycle" do
|
104
140
|
subject.start
|
141
|
+
assert_equal 1, @on_start_called_at
|
142
|
+
assert_equal 2, @on_sleep_called_at
|
143
|
+
@queue.push('work')
|
144
|
+
assert_equal 3, @on_wakeup_called_at
|
145
|
+
assert_equal 4, @before_work_called_at
|
146
|
+
assert_equal 5, @after_work_called_at
|
147
|
+
assert_equal 6, @on_sleep_called_at
|
105
148
|
subject.shutdown
|
106
149
|
@queue.shutdown
|
107
|
-
|
108
|
-
|
109
|
-
assert_equal true, shutdown
|
110
|
-
assert_equal subject, yielded_worker
|
150
|
+
assert_equal 7, @on_wakeup_called_at
|
151
|
+
assert_equal 8, @on_shutdown_called_at
|
111
152
|
end
|
112
153
|
|
113
154
|
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: 15
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 4
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.4.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Collin Redding
|
@@ -16,10 +16,10 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date:
|
19
|
+
date: 2015-01-02 00:00:00 Z
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
|
-
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
23
|
none: false
|
24
24
|
requirements:
|
25
25
|
- - ~>
|
@@ -29,24 +29,25 @@ dependencies:
|
|
29
29
|
- 1
|
30
30
|
- 2
|
31
31
|
version: "1.2"
|
32
|
+
version_requirements: *id001
|
32
33
|
type: :runtime
|
33
|
-
requirement: *id001
|
34
|
-
prerelease: false
|
35
34
|
name: SystemTimer
|
35
|
+
prerelease: false
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
|
-
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
38
|
none: false
|
39
39
|
requirements:
|
40
|
-
- -
|
40
|
+
- - ~>
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
hash:
|
42
|
+
hash: 27
|
43
43
|
segments:
|
44
|
-
-
|
45
|
-
|
44
|
+
- 2
|
45
|
+
- 12
|
46
|
+
version: "2.12"
|
47
|
+
version_requirements: *id002
|
46
48
|
type: :development
|
47
|
-
requirement: *id002
|
48
|
-
prerelease: false
|
49
49
|
name: assert
|
50
|
+
prerelease: false
|
50
51
|
description: A simple thread pool for processing generic 'work'
|
51
52
|
email:
|
52
53
|
- collin.redding@me.com
|
@@ -106,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
107
|
requirements: []
|
107
108
|
|
108
109
|
rubyforge_project:
|
109
|
-
rubygems_version: 1.8.
|
110
|
+
rubygems_version: 1.8.29
|
110
111
|
signing_key:
|
111
112
|
specification_version: 3
|
112
113
|
summary: A simple thread pool for processing generic 'work'
|