dat-worker-pool 0.5.0 → 0.6.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/Gemfile +1 -1
- data/bench/report.rb +130 -0
- data/bench/report.txt +7 -0
- data/dat-worker-pool.gemspec +1 -1
- data/lib/dat-worker-pool.rb +38 -187
- data/lib/dat-worker-pool/default_queue.rb +60 -0
- data/lib/dat-worker-pool/locked_object.rb +60 -0
- data/lib/dat-worker-pool/queue.rb +71 -48
- data/lib/dat-worker-pool/runner.rb +196 -0
- data/lib/dat-worker-pool/version.rb +1 -1
- data/lib/dat-worker-pool/worker.rb +251 -72
- data/lib/dat-worker-pool/worker_pool_spy.rb +39 -53
- data/test/helper.rb +13 -0
- data/test/support/factory.rb +15 -0
- data/test/support/thread_spies.rb +83 -0
- data/test/system/dat-worker-pool_tests.rb +399 -0
- data/test/unit/dat-worker-pool_tests.rb +132 -255
- data/test/unit/default_queue_tests.rb +217 -0
- data/test/unit/locked_object_tests.rb +260 -0
- data/test/unit/queue_tests.rb +95 -72
- data/test/unit/runner_tests.rb +365 -0
- data/test/unit/worker_pool_spy_tests.rb +95 -102
- data/test/unit/worker_tests.rb +819 -153
- metadata +27 -12
- data/test/system/use_worker_pool_tests.rb +0 -34
data/test/unit/queue_tests.rb
CHANGED
@@ -1,108 +1,131 @@
|
|
1
1
|
require 'assert'
|
2
2
|
require 'dat-worker-pool/queue'
|
3
3
|
|
4
|
-
|
4
|
+
module DatWorkerPool::Queue
|
5
5
|
|
6
6
|
class UnitTests < Assert::Context
|
7
7
|
desc "DatWorkerPool::Queue"
|
8
8
|
setup do
|
9
|
-
@
|
10
|
-
|
11
|
-
subject{ @queue }
|
9
|
+
@queue_class = Class.new do
|
10
|
+
include DatWorkerPool::Queue
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
attr_reader :start_called, :shutdown_called
|
13
|
+
attr_reader :push_called_with
|
14
|
+
attr_accessor :pop_result
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
def initialize
|
17
|
+
@start_called = false
|
18
|
+
@shutdown_called = false
|
19
|
+
@push_called_with = nil
|
20
|
+
@pop_result = nil
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
assert_equal [ 'work' ], subject.work_items
|
25
|
-
end
|
23
|
+
def start!; @start_called = true; end
|
24
|
+
def shutdown!; @shutdown_called = true; end
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
subject.push 'work'
|
31
|
-
assert_true on_push_called
|
26
|
+
def push!(*args); @push_called_with = args; end
|
27
|
+
def pop!; @pop_result; end
|
28
|
+
end
|
32
29
|
end
|
30
|
+
subject{ @queue_class }
|
33
31
|
|
34
|
-
should "raise
|
35
|
-
|
36
|
-
|
32
|
+
should "raise a not implemented error for `dwp_push` and `dwp_pop` by default" do
|
33
|
+
queue_class = Class.new{ include DatWorkerPool::Queue }
|
34
|
+
queue = queue_class.new.tap(&:dwp_start)
|
35
|
+
|
36
|
+
assert_raises(NotImplementedError){ queue.dwp_push(Factory.string) }
|
37
|
+
assert_raises(NotImplementedError){ queue.dwp_pop }
|
37
38
|
end
|
38
39
|
|
39
|
-
|
40
|
-
subject.push 'work1'
|
41
|
-
subject.push 'work2'
|
40
|
+
end
|
42
41
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
assert_equal 0, subject.work_items.size
|
42
|
+
class InitTests < UnitTests
|
43
|
+
desc "when init"
|
44
|
+
setup do
|
45
|
+
@queue = @queue_class.new
|
48
46
|
end
|
47
|
+
subject{ @queue }
|
48
|
+
|
49
|
+
should have_imeths :work_items
|
50
|
+
should have_imeths :dwp_start, :dwp_signal_shutdown, :dwp_shutdown
|
51
|
+
should have_imeths :running?, :shutdown?
|
52
|
+
should have_imeths :dwp_push, :dwp_pop
|
49
53
|
|
50
|
-
should "
|
51
|
-
subject.
|
52
|
-
on_pop_called = false
|
53
|
-
subject.on_pop_callbacks << proc{ on_pop_called = true }
|
54
|
-
subject.pop
|
55
|
-
assert_true on_pop_called
|
54
|
+
should "raise a not implemented error using `work_items`" do
|
55
|
+
assert_raises(NotImplementedError){ subject.work_items }
|
56
56
|
end
|
57
57
|
|
58
|
-
should "
|
59
|
-
subject.
|
60
|
-
subject.shutdown
|
61
|
-
|
58
|
+
should "set its flags using `dwp_start` and `dwp_shutdown`" do
|
59
|
+
assert_false subject.running?
|
60
|
+
assert_true subject.shutdown?
|
61
|
+
subject.dwp_start
|
62
|
+
assert_true subject.running?
|
63
|
+
assert_false subject.shutdown?
|
64
|
+
subject.dwp_shutdown
|
65
|
+
assert_false subject.running?
|
66
|
+
assert_true subject.shutdown?
|
62
67
|
end
|
63
68
|
|
64
|
-
should "
|
65
|
-
|
66
|
-
subject.
|
67
|
-
|
68
|
-
subject.pop
|
69
|
-
assert subject.empty?
|
69
|
+
should "call `start!` using `dwp_start`" do
|
70
|
+
assert_false subject.start_called
|
71
|
+
subject.dwp_start
|
72
|
+
assert_true subject.start_called
|
70
73
|
end
|
71
74
|
|
72
|
-
should "
|
73
|
-
assert_false subject.
|
74
|
-
subject.
|
75
|
-
|
76
|
-
subject.
|
77
|
-
assert_false subject.
|
75
|
+
should "set its shutdown flag using `dwp_signal_shutdown`" do
|
76
|
+
assert_false subject.running?
|
77
|
+
assert_false subject.shutdown_called
|
78
|
+
subject.dwp_start
|
79
|
+
assert_true subject.running?
|
80
|
+
assert_false subject.shutdown_called
|
81
|
+
subject.dwp_signal_shutdown
|
82
|
+
assert_false subject.running?
|
83
|
+
assert_false subject.shutdown_called
|
78
84
|
end
|
79
85
|
|
80
|
-
|
86
|
+
should "call `shutdown!` using `dwp_shutdown`" do
|
87
|
+
assert_false subject.shutdown_called
|
88
|
+
subject.dwp_shutdown
|
89
|
+
assert_true subject.shutdown_called
|
90
|
+
end
|
81
91
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
92
|
+
should "raise an error if `dwp_push` is called when the queue isn't running" do
|
93
|
+
assert_false subject.running?
|
94
|
+
assert_raise(RuntimeError){ subject.dwp_push(Factory.string) }
|
95
|
+
subject.dwp_start
|
96
|
+
assert_nothing_raised{ subject.dwp_push(Factory.string) }
|
97
|
+
subject.dwp_shutdown
|
98
|
+
assert_raise(RuntimeError){ subject.dwp_push(Factory.string) }
|
88
99
|
end
|
89
100
|
|
90
|
-
should "
|
91
|
-
|
101
|
+
should "call `push!` using `dwp_push`" do
|
102
|
+
subject.dwp_start
|
103
|
+
|
104
|
+
work_item = Factory.string
|
105
|
+
subject.dwp_push(work_item)
|
106
|
+
assert_equal [work_item], subject.push_called_with
|
107
|
+
|
108
|
+
args = Factory.integer(3).times.map{ Factory.string }
|
109
|
+
subject.dwp_push(*args)
|
110
|
+
assert_equal args, subject.push_called_with
|
92
111
|
end
|
93
112
|
|
94
|
-
should "
|
95
|
-
subject.
|
96
|
-
|
97
|
-
|
98
|
-
|
113
|
+
should "return nothing if `dwp_pop` is called when the queue isn't running" do
|
114
|
+
subject.pop_result = Factory.string
|
115
|
+
assert_false subject.running?
|
116
|
+
assert_nil subject.dwp_pop
|
117
|
+
subject.dwp_start
|
118
|
+
assert_not_nil subject.dwp_pop
|
119
|
+
subject.dwp_shutdown
|
120
|
+
assert_nil subject.dwp_pop
|
99
121
|
end
|
100
122
|
|
101
|
-
should "
|
102
|
-
subject.
|
103
|
-
|
104
|
-
|
105
|
-
|
123
|
+
should "call `pop!` using `dwp_pop`" do
|
124
|
+
subject.dwp_start
|
125
|
+
subject.pop_result = Factory.string
|
126
|
+
|
127
|
+
value = subject.dwp_pop
|
128
|
+
assert_equal subject.pop_result, value
|
106
129
|
end
|
107
130
|
|
108
131
|
end
|
@@ -0,0 +1,365 @@
|
|
1
|
+
require 'assert'
|
2
|
+
require 'dat-worker-pool/runner'
|
3
|
+
|
4
|
+
require 'dat-worker-pool/default_queue'
|
5
|
+
|
6
|
+
class DatWorkerPool::Runner
|
7
|
+
|
8
|
+
class UnitTests < Assert::Context
|
9
|
+
desc "DatWorkerPool::Runner"
|
10
|
+
setup do
|
11
|
+
@runner_class = DatWorkerPool::Runner
|
12
|
+
end
|
13
|
+
subject{ @runner_class }
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
class InitTests < UnitTests
|
18
|
+
desc "when init"
|
19
|
+
setup do
|
20
|
+
# at least 2 workers, up to 4
|
21
|
+
@num_workers = Factory.integer(3) + 1
|
22
|
+
@logger = TEST_LOGGER || Logger.new("/dev/null")
|
23
|
+
@queue = DatWorkerPool::DefaultQueue.new
|
24
|
+
@worker_class = TestWorker
|
25
|
+
@worker_params = { Factory.string => Factory.string }
|
26
|
+
|
27
|
+
@workers = DatWorkerPool::LockedArray.new
|
28
|
+
Assert.stub(DatWorkerPool::LockedArray, :new){ @workers }
|
29
|
+
|
30
|
+
@available_workers_spy = DatWorkerPool::LockedSet.new
|
31
|
+
Assert.stub(DatWorkerPool::LockedSet, :new){ @available_workers_spy }
|
32
|
+
|
33
|
+
@options = {
|
34
|
+
:num_workers => @num_workers,
|
35
|
+
:logger => @logger,
|
36
|
+
:queue => @queue,
|
37
|
+
:worker_class => @worker_class,
|
38
|
+
:worker_params => @worker_params
|
39
|
+
}
|
40
|
+
@runner = @runner_class.new(@options)
|
41
|
+
end
|
42
|
+
teardown do
|
43
|
+
@runner.shutdown(0) rescue false
|
44
|
+
end
|
45
|
+
subject{ @runner }
|
46
|
+
|
47
|
+
should have_readers :num_workers, :worker_class, :worker_params
|
48
|
+
should have_readers :logger_proxy, :queue
|
49
|
+
should have_imeths :workers, :start, :shutdown
|
50
|
+
should have_imeths :available_worker_count, :worker_available?
|
51
|
+
should have_imeths :make_worker_available, :make_worker_unavailable
|
52
|
+
should have_imeths :worker_log
|
53
|
+
|
54
|
+
should "know its attributes" do
|
55
|
+
assert_equal @num_workers, subject.num_workers
|
56
|
+
assert_equal @worker_class, subject.worker_class
|
57
|
+
assert_equal @worker_params, subject.worker_params
|
58
|
+
assert_equal @queue, subject.queue
|
59
|
+
|
60
|
+
assert_instance_of LoggerProxy, subject.logger_proxy
|
61
|
+
assert_equal @logger, subject.logger_proxy.logger
|
62
|
+
end
|
63
|
+
|
64
|
+
should "default its logger" do
|
65
|
+
@options.delete(:logger)
|
66
|
+
runner = @runner_class.new(@options)
|
67
|
+
assert_instance_of NullLoggerProxy, runner.logger_proxy
|
68
|
+
end
|
69
|
+
|
70
|
+
should "know its workers" do
|
71
|
+
assert_equal @workers.values, subject.workers
|
72
|
+
@workers.push(Factory.string)
|
73
|
+
assert_equal @workers.values, subject.workers
|
74
|
+
end
|
75
|
+
|
76
|
+
should "start its queue when its started" do
|
77
|
+
assert_false @queue.running?
|
78
|
+
subject.start
|
79
|
+
assert_true @queue.running?
|
80
|
+
end
|
81
|
+
|
82
|
+
should "build and add workers when its started" do
|
83
|
+
subject.start
|
84
|
+
|
85
|
+
assert_equal @num_workers, subject.workers.size
|
86
|
+
subject.workers.each_with_index do |worker, n|
|
87
|
+
assert_equal subject, worker.dwp_runner
|
88
|
+
assert_equal @queue, worker.dwp_queue
|
89
|
+
assert_equal n + 1, worker.dwp_number
|
90
|
+
assert_true worker.dwp_running?
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
should "allow making workers available/unavailable" do
|
95
|
+
worker = @worker_class.new(@runner, @queue, Factory.integer(10))
|
96
|
+
|
97
|
+
assert_not_includes worker.object_id, @available_workers_spy.values
|
98
|
+
assert_false subject.worker_available?
|
99
|
+
subject.make_worker_available(worker)
|
100
|
+
assert_includes worker.object_id, @available_workers_spy.values
|
101
|
+
assert_true subject.worker_available?
|
102
|
+
subject.make_worker_unavailable(worker)
|
103
|
+
assert_not_includes worker.object_id, @available_workers_spy.values
|
104
|
+
assert_false subject.worker_available?
|
105
|
+
end
|
106
|
+
|
107
|
+
should "know how many workers are available" do
|
108
|
+
worker = @worker_class.new(@runner, @queue, Factory.integer(10))
|
109
|
+
|
110
|
+
assert_equal 0, subject.available_worker_count
|
111
|
+
subject.make_worker_available(worker)
|
112
|
+
assert_equal 1, subject.available_worker_count
|
113
|
+
subject.make_worker_unavailable(worker)
|
114
|
+
assert_equal 0, subject.available_worker_count
|
115
|
+
end
|
116
|
+
|
117
|
+
should "allow logging messages using `log`" do
|
118
|
+
logged_message = nil
|
119
|
+
Assert.stub(subject.logger_proxy, :runner_log) do |&mb|
|
120
|
+
logged_message = mb.call
|
121
|
+
end
|
122
|
+
|
123
|
+
text = Factory.text
|
124
|
+
subject.log{ text }
|
125
|
+
assert_equal text, logged_message
|
126
|
+
end
|
127
|
+
|
128
|
+
should "allow workers to log messages using `worker_log`" do
|
129
|
+
passed_worker = nil
|
130
|
+
logged_message = nil
|
131
|
+
Assert.stub(subject.logger_proxy, :worker_log) do |w, &mb|
|
132
|
+
passed_worker = w
|
133
|
+
logged_message = mb.call
|
134
|
+
end
|
135
|
+
worker = @worker_class.new(@runner, @queue, Factory.integer(10))
|
136
|
+
|
137
|
+
text = Factory.text
|
138
|
+
subject.worker_log(worker){ text }
|
139
|
+
assert_same worker, passed_worker
|
140
|
+
assert_equal text, logged_message
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
class ShutdownSetupTests < InitTests
|
146
|
+
desc "and started and shutdown"
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
class ShutdownTests < ShutdownSetupTests
|
151
|
+
setup do
|
152
|
+
@timeout_seconds = nil
|
153
|
+
@optional_timeout_called = false
|
154
|
+
# this acts as a spy but also keeps the shutdown from ever timing out
|
155
|
+
Assert.stub(OptionalTimeout, :new) do |secs, &block|
|
156
|
+
@timeout_seconds = secs
|
157
|
+
@optional_timeout_called = true
|
158
|
+
block.call
|
159
|
+
end
|
160
|
+
|
161
|
+
@options[:worker_class] = ShutdownSpyWorker
|
162
|
+
@runner = @runner_class.new(@options)
|
163
|
+
@runner.start
|
164
|
+
# we need a reference to the workers, the runners workers will get removed
|
165
|
+
# as they shutdown
|
166
|
+
@running_workers = @runner.workers.dup
|
167
|
+
end
|
168
|
+
|
169
|
+
should "optionally timeout when shutdown" do
|
170
|
+
subject.shutdown
|
171
|
+
assert_nil @timeout_seconds
|
172
|
+
assert_true @optional_timeout_called
|
173
|
+
|
174
|
+
@optional_timeout_called = false
|
175
|
+
seconds = Factory.integer
|
176
|
+
subject.shutdown(seconds)
|
177
|
+
assert_equal seconds, @timeout_seconds
|
178
|
+
assert_true @optional_timeout_called
|
179
|
+
end
|
180
|
+
|
181
|
+
should "shutdown all of its workers" do
|
182
|
+
@running_workers.each do |worker|
|
183
|
+
assert_false worker.dwp_shutdown?
|
184
|
+
end
|
185
|
+
subject.shutdown(Factory.boolean ? Factory.integer : nil)
|
186
|
+
@running_workers.each do |worker|
|
187
|
+
assert_true worker.dwp_shutdown?
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
should "shutdown its queue" do
|
192
|
+
assert_false @queue.shutdown?
|
193
|
+
subject.shutdown(Factory.boolean ? Factory.integer : nil)
|
194
|
+
assert_true @queue.shutdown?
|
195
|
+
end
|
196
|
+
|
197
|
+
should "join its workers waiting for them to finish" do
|
198
|
+
@running_workers.each do |worker|
|
199
|
+
assert_false worker.join_called
|
200
|
+
end
|
201
|
+
subject.shutdown(Factory.boolean ? Factory.integer : nil)
|
202
|
+
@running_workers.each do |worker|
|
203
|
+
assert_true worker.join_called
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
should "join all workers even if one raises an error when joined" do
|
208
|
+
@running_workers.choice.join_error = Factory.exception
|
209
|
+
subject.shutdown(Factory.boolean ? Factory.integer : nil)
|
210
|
+
@running_workers.each do |worker|
|
211
|
+
assert_true worker.join_called
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
should "remove workers as they finish" do
|
216
|
+
assert_false subject.workers.empty?
|
217
|
+
subject.shutdown(Factory.boolean ? Factory.integer : nil)
|
218
|
+
assert_true subject.workers.empty?
|
219
|
+
end
|
220
|
+
|
221
|
+
should "remove workers and make them unavailable even if they error" do
|
222
|
+
@running_workers.each{ |w| w.join_error = Factory.exception }
|
223
|
+
|
224
|
+
assert_false subject.workers.empty?
|
225
|
+
assert_false @available_workers_spy.empty?
|
226
|
+
subject.shutdown(Factory.boolean ? Factory.integer : nil)
|
227
|
+
assert_true subject.workers.empty?
|
228
|
+
assert_true @available_workers_spy.empty?
|
229
|
+
end
|
230
|
+
|
231
|
+
should "force its workers to shutdown if a timeout error occurs" do
|
232
|
+
Assert.stub(OptionalTimeout, :new){ raise TimeoutInterruptError }
|
233
|
+
subject.shutdown(Factory.integer)
|
234
|
+
|
235
|
+
@running_workers.each do |worker|
|
236
|
+
assert_instance_of DatWorkerPool::ShutdownError, worker.raised_error
|
237
|
+
assert_true worker.join_called
|
238
|
+
end
|
239
|
+
assert_true subject.workers.empty?
|
240
|
+
assert_true @available_workers_spy.empty?
|
241
|
+
end
|
242
|
+
|
243
|
+
should "force its workers to shutdown if a non-timeout error occurs" do
|
244
|
+
queue_exception = Factory.exception
|
245
|
+
Assert.stub(@queue, :dwp_shutdown){ raise queue_exception }
|
246
|
+
|
247
|
+
caught_exception = nil
|
248
|
+
begin
|
249
|
+
subject.shutdown(Factory.integer)
|
250
|
+
rescue StandardError => caught_exception
|
251
|
+
end
|
252
|
+
assert_same queue_exception, caught_exception
|
253
|
+
|
254
|
+
@running_workers.each do |worker|
|
255
|
+
assert_instance_of DatWorkerPool::ShutdownError, worker.raised_error
|
256
|
+
assert_true worker.join_called
|
257
|
+
end
|
258
|
+
assert_true subject.workers.empty?
|
259
|
+
assert_true @available_workers_spy.empty?
|
260
|
+
end
|
261
|
+
|
262
|
+
should "force shutdown all of its workers even if one raises an error when joining" do
|
263
|
+
Assert.stub(OptionalTimeout, :new){ raise TimeoutInterruptError }
|
264
|
+
error_class = Factory.boolean ? DatWorkerPool::ShutdownError : RuntimeError
|
265
|
+
@running_workers.choice.join_error = Factory.exception(error_class)
|
266
|
+
subject.shutdown(Factory.boolean ? Factory.integer : nil)
|
267
|
+
|
268
|
+
@running_workers.each do |worker|
|
269
|
+
assert_instance_of DatWorkerPool::ShutdownError, worker.raised_error
|
270
|
+
assert_true worker.join_called
|
271
|
+
end
|
272
|
+
assert_true subject.workers.empty?
|
273
|
+
assert_true @available_workers_spy.empty?
|
274
|
+
end
|
275
|
+
|
276
|
+
end
|
277
|
+
|
278
|
+
class LoggerProxyTests < UnitTests
|
279
|
+
desc "LoggerProxy"
|
280
|
+
setup do
|
281
|
+
@stringio = StringIO.new
|
282
|
+
@logger = Logger.new(@stringio)
|
283
|
+
@logger_proxy = LoggerProxy.new(@logger)
|
284
|
+
end
|
285
|
+
subject{ @logger_proxy }
|
286
|
+
|
287
|
+
should have_readers :logger
|
288
|
+
should have_imeths :runner_log, :worker_log
|
289
|
+
|
290
|
+
should "know its logger" do
|
291
|
+
assert_equal @logger, subject.logger
|
292
|
+
end
|
293
|
+
|
294
|
+
should "log a message block for a runner using `runner_log`" do
|
295
|
+
text = Factory.text
|
296
|
+
subject.runner_log{ text }
|
297
|
+
assert_match "[DWP] #{text}", @stringio.string
|
298
|
+
end
|
299
|
+
|
300
|
+
should "log a message block for a worker using `worker_log`" do
|
301
|
+
worker = FakeWorker.new(Factory.integer(10))
|
302
|
+
text = Factory.text
|
303
|
+
subject.worker_log(worker){ text }
|
304
|
+
assert_match "[DWP-#{worker.dwp_number}] #{text}", @stringio.string
|
305
|
+
end
|
306
|
+
|
307
|
+
end
|
308
|
+
|
309
|
+
class NullLoggerProxyTests < UnitTests
|
310
|
+
desc "NullLoggerProxy"
|
311
|
+
setup do
|
312
|
+
@null_logger_proxy = NullLoggerProxy.new
|
313
|
+
end
|
314
|
+
subject{ @null_logger_proxy }
|
315
|
+
|
316
|
+
should have_imeths :runner_log, :worker_log
|
317
|
+
|
318
|
+
end
|
319
|
+
|
320
|
+
class TestWorker
|
321
|
+
include DatWorkerPool::Worker
|
322
|
+
|
323
|
+
# for testing what is passed to the worker
|
324
|
+
attr_reader :dwp_runner, :dwp_queue
|
325
|
+
end
|
326
|
+
|
327
|
+
FakeWorker = Struct.new(:dwp_number)
|
328
|
+
|
329
|
+
class ShutdownSpyWorker < TestWorker
|
330
|
+
attr_reader :join_called
|
331
|
+
attr_accessor :join_error, :raised_error
|
332
|
+
|
333
|
+
# this pauses the shutdown so we can test that join or raise are called
|
334
|
+
# depending if we are doing a standard or forced shutdown; otherwise the
|
335
|
+
# worker threads can exit before join or raise ever gets called on them and
|
336
|
+
# then there is nothing to test
|
337
|
+
on_shutdown{ wait_for_join_or_raise }
|
338
|
+
|
339
|
+
def initialize(*args)
|
340
|
+
super
|
341
|
+
@mutex = Mutex.new
|
342
|
+
@cond_var = ConditionVariable.new
|
343
|
+
@join_called = false
|
344
|
+
@join_error = nil
|
345
|
+
@raised_error = nil
|
346
|
+
end
|
347
|
+
|
348
|
+
def dwp_join(*args)
|
349
|
+
@join_called = true
|
350
|
+
raise @join_error if @join_error
|
351
|
+
@mutex.synchronize{ @cond_var.broadcast }
|
352
|
+
end
|
353
|
+
|
354
|
+
def dwp_raise(error)
|
355
|
+
@raised_error = error
|
356
|
+
end
|
357
|
+
|
358
|
+
private
|
359
|
+
|
360
|
+
def wait_for_join_or_raise
|
361
|
+
@mutex.synchronize{ @cond_var.wait(@mutex) }
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
end
|