dat-worker-pool 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -20,6 +20,6 @@ Gem::Specification.new do |gem|
20
20
 
21
21
  gem.add_dependency("SystemTimer", ["~> 1.2"])
22
22
 
23
- gem.add_development_dependency("assert")
23
+ gem.add_development_dependency("assert", ["~> 2.12"])
24
24
 
25
25
  end
@@ -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
- @min_workers.times{ self.spawn_worker }
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 = 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) }
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
@@ -1,3 +1,3 @@
1
1
  class DatWorkerPool
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -3,14 +3,21 @@ 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
+ 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 = proc{ |work_item| }
11
- @on_waiting = proc{ |worker| }
12
- @on_continuing = proc{ |worker| }
13
- @on_shutdown = proc{ |worker| }
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
- @on_waiting.call(self)
47
+ @on_sleep_callbacks.each{ |p| p.call(self) }
40
48
  work_item = @queue.pop
41
- @on_continuing.call(self)
49
+ @on_wakeup_callbacks.each{ |p| p.call(self) }
42
50
  break if @shutdown
43
- @on_work.call(work_item) if work_item
51
+ do_work(work_item) if work_item
44
52
  end
45
53
  ensure
46
- @on_shutdown.call(self)
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 :work_items
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
- @work_items << work if work
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 shutdown(timeout)
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 have_imeths :add_work, :shutdown, :despawn_worker
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'
@@ -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
- @worker_pool_spy = DatWorkerPool::WorkerPoolSpy.new
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
- assert subject.shutdown_called
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
@@ -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 have_writers :on_waiting, :on_continuing, :on_shutdown
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.on_work = proc{ |work| sleep 0.2 }
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 "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
117
+ should "pass its self to its start, shutdown, sleep and wakupe callbacks" do
75
118
  subject.start
76
- subject.join 0.1 # trigger the worker's thread to run
119
+ @queue.push('work')
120
+ subject.shutdown
121
+ @queue.shutdown
77
122
 
78
- assert_equal true, waiting
79
- assert_equal subject, yielded_worker
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 "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
129
+ should "pass its self and work to its beofre and after work callbacks" do
89
130
  subject.start
90
- @queue.push 'some work'
91
- subject.join 0.1 # trigger the worker's thread to run
131
+ @queue.push('work')
132
+ subject.shutdown
133
+ @queue.shutdown
92
134
 
93
- assert_equal false, waiting
94
- assert_equal subject, yielded_worker
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 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
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
- subject.join 0.1 # trigger the worker's thread to run
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: 19
4
+ hash: 15
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 3
8
+ - 4
9
9
  - 0
10
- version: 0.3.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: 2013-10-10 00:00:00 Z
19
+ date: 2015-01-02 00:00:00 Z
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- version_requirements: &id001 !ruby/object:Gem::Requirement
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
- version_requirements: &id002 !ruby/object:Gem::Requirement
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
38
  none: false
39
39
  requirements:
40
- - - ">="
40
+ - - ~>
41
41
  - !ruby/object:Gem::Version
42
- hash: 3
42
+ hash: 27
43
43
  segments:
44
- - 0
45
- version: "0"
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.15
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'