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.
@@ -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'