concurrent-ruby 0.6.1 → 0.7.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/concurrent.rb +3 -4
  4. data/lib/concurrent/atomic.rb +46 -0
  5. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +7 -0
  6. data/lib/concurrent/atomic_reference/delegated_update.rb +28 -0
  7. data/lib/concurrent/atomic_reference/direct_update.rb +28 -0
  8. data/lib/concurrent/atomic_reference/jruby.rb +8 -0
  9. data/lib/concurrent/atomic_reference/mutex_atomic.rb +47 -0
  10. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +24 -0
  11. data/lib/concurrent/atomic_reference/rbx.rb +16 -0
  12. data/lib/concurrent/atomic_reference/ruby.rb +16 -0
  13. data/lib/concurrent/atomics.rb +1 -1
  14. data/lib/concurrent/configuration.rb +1 -1
  15. data/lib/concurrent/supervisor.rb +1 -1
  16. data/lib/concurrent/timer_task.rb +0 -36
  17. data/lib/concurrent/version.rb +1 -1
  18. data/lib/concurrent_ruby_ext.so +0 -0
  19. data/lib/extension_helper.rb +9 -0
  20. metadata +16 -148
  21. data/lib/concurrent/actor/actor.rb +0 -270
  22. data/lib/concurrent/actor/postable.rb +0 -102
  23. data/lib/concurrent/actors.rb +0 -2
  24. data/lib/concurrent/atomic/atomic.rb +0 -48
  25. data/lib/concurrent/runnable.rb +0 -90
  26. data/lib/concurrent/stoppable.rb +0 -20
  27. data/spec/concurrent/actor/actor_spec.rb +0 -376
  28. data/spec/concurrent/actor/postable_shared.rb +0 -218
  29. data/spec/concurrent/actress_spec.rb +0 -211
  30. data/spec/concurrent/agent_spec.rb +0 -500
  31. data/spec/concurrent/async_spec.rb +0 -352
  32. data/spec/concurrent/atomic/atomic_boolean_spec.rb +0 -172
  33. data/spec/concurrent/atomic/atomic_fixnum_spec.rb +0 -186
  34. data/spec/concurrent/atomic/atomic_spec.rb +0 -133
  35. data/spec/concurrent/atomic/condition_spec.rb +0 -171
  36. data/spec/concurrent/atomic/copy_on_notify_observer_set_spec.rb +0 -10
  37. data/spec/concurrent/atomic/copy_on_write_observer_set_spec.rb +0 -10
  38. data/spec/concurrent/atomic/count_down_latch_spec.rb +0 -151
  39. data/spec/concurrent/atomic/cyclic_barrier_spec.rb +0 -248
  40. data/spec/concurrent/atomic/event_spec.rb +0 -200
  41. data/spec/concurrent/atomic/observer_set_shared.rb +0 -242
  42. data/spec/concurrent/atomic/thread_local_var_spec.rb +0 -113
  43. data/spec/concurrent/channel/buffered_channel_spec.rb +0 -151
  44. data/spec/concurrent/channel/channel_spec.rb +0 -39
  45. data/spec/concurrent/channel/probe_spec.rb +0 -77
  46. data/spec/concurrent/channel/unbuffered_channel_spec.rb +0 -132
  47. data/spec/concurrent/collection/blocking_ring_buffer_spec.rb +0 -149
  48. data/spec/concurrent/collection/priority_queue_spec.rb +0 -317
  49. data/spec/concurrent/collection/ring_buffer_spec.rb +0 -126
  50. data/spec/concurrent/configuration_spec.rb +0 -69
  51. data/spec/concurrent/dataflow_spec.rb +0 -242
  52. data/spec/concurrent/delay_spec.rb +0 -91
  53. data/spec/concurrent/dereferenceable_shared.rb +0 -146
  54. data/spec/concurrent/exchanger_spec.rb +0 -66
  55. data/spec/concurrent/executor/cached_thread_pool_shared.rb +0 -115
  56. data/spec/concurrent/executor/fixed_thread_pool_shared.rb +0 -136
  57. data/spec/concurrent/executor/global_thread_pool_shared.rb +0 -35
  58. data/spec/concurrent/executor/immediate_executor_spec.rb +0 -12
  59. data/spec/concurrent/executor/java_cached_thread_pool_spec.rb +0 -44
  60. data/spec/concurrent/executor/java_fixed_thread_pool_spec.rb +0 -64
  61. data/spec/concurrent/executor/java_single_thread_executor_spec.rb +0 -21
  62. data/spec/concurrent/executor/java_thread_pool_executor_spec.rb +0 -71
  63. data/spec/concurrent/executor/per_thread_executor_spec.rb +0 -57
  64. data/spec/concurrent/executor/ruby_cached_thread_pool_spec.rb +0 -69
  65. data/spec/concurrent/executor/ruby_fixed_thread_pool_spec.rb +0 -39
  66. data/spec/concurrent/executor/ruby_single_thread_executor_spec.rb +0 -18
  67. data/spec/concurrent/executor/ruby_thread_pool_executor_spec.rb +0 -171
  68. data/spec/concurrent/executor/safe_task_executor_spec.rb +0 -103
  69. data/spec/concurrent/executor/thread_pool_class_cast_spec.rb +0 -52
  70. data/spec/concurrent/executor/thread_pool_executor_shared.rb +0 -155
  71. data/spec/concurrent/executor/thread_pool_shared.rb +0 -269
  72. data/spec/concurrent/executor/timer_set_spec.rb +0 -183
  73. data/spec/concurrent/future_spec.rb +0 -329
  74. data/spec/concurrent/ivar_spec.rb +0 -215
  75. data/spec/concurrent/mvar_spec.rb +0 -380
  76. data/spec/concurrent/obligation_shared.rb +0 -102
  77. data/spec/concurrent/obligation_spec.rb +0 -282
  78. data/spec/concurrent/observable_shared.rb +0 -177
  79. data/spec/concurrent/observable_spec.rb +0 -56
  80. data/spec/concurrent/options_parser_spec.rb +0 -71
  81. data/spec/concurrent/promise_spec.rb +0 -367
  82. data/spec/concurrent/runnable_shared.rb +0 -68
  83. data/spec/concurrent/runnable_spec.rb +0 -235
  84. data/spec/concurrent/scheduled_task_spec.rb +0 -340
  85. data/spec/concurrent/stoppable_shared.rb +0 -37
  86. data/spec/concurrent/supervisor_spec.rb +0 -1149
  87. data/spec/concurrent/timer_task_spec.rb +0 -256
  88. data/spec/concurrent/tvar_spec.rb +0 -137
  89. data/spec/concurrent/utility/processor_count_spec.rb +0 -20
  90. data/spec/concurrent/utility/timeout_spec.rb +0 -50
  91. data/spec/concurrent/utility/timer_spec.rb +0 -52
  92. data/spec/spec_helper.rb +0 -41
  93. data/spec/support/example_group_extensions.rb +0 -52
  94. data/spec/support/less_than_or_equal_to_matcher.rb +0 -5
@@ -1,37 +0,0 @@
1
- require 'spec_helper'
2
-
3
- share_examples_for :stoppable do
4
-
5
- after(:each) do
6
- subject.stop
7
- end
8
-
9
- context 'stopping' do
10
-
11
- it 'raises an exception when #before_stop does not receive a block' do
12
- expect {
13
- subject.before_stop
14
- }.to raise_error(ArgumentError)
15
- end
16
-
17
- it 'raises an exception if #before_stop is called more than once' do
18
- subject.before_stop{ nil }
19
- expect {
20
- subject.before_stop{ nil }
21
- }.to raise_error(Concurrent::Runnable::LifecycleError)
22
- end
23
-
24
- it 'returns self from #before_stop' do
25
- task = subject
26
- task.before_stop{ nil }.should eq task
27
- end
28
-
29
- it 'calls the #before_stop block when stopping' do
30
- @expected = false
31
- subject.before_stop{ @expected = true }
32
- subject.stop
33
- sleep(0.1)
34
- @expected.should be_true
35
- end
36
- end
37
- end
@@ -1,1149 +0,0 @@
1
- require 'spec_helper'
2
- require 'timecop'
3
- require_relative 'runnable_shared'
4
-
5
- module Concurrent
6
-
7
- describe Supervisor do
8
-
9
- let(:worker_class) do
10
- Class.new {
11
- attr_reader :start_count, :stop_count
12
- def run() @start_count ||= 0; @start_count += 1; return true; end
13
- def stop() @stop_count ||= 0; @stop_count += 1; return true; end
14
- def running?() return true; end
15
- }
16
- end
17
-
18
- let(:sleeper_class) do
19
- Class.new(worker_class) {
20
- def run() super(); sleep; end
21
- }
22
- end
23
-
24
- let(:stopper_class) do
25
- Class.new(worker_class) {
26
- attr_reader :latch
27
- def initialize(sleep_time = 0.2)
28
- @sleep_time = sleep_time
29
- @latch = Concurrent::CountDownLatch.new(1)
30
- end
31
- def run
32
- super
33
- sleep(@sleep_time)
34
- @latch.count_down
35
- end
36
- }
37
- end
38
-
39
- let(:error_class) do
40
- Class.new(worker_class) {
41
- def run() super(); raise StandardError; end
42
- }
43
- end
44
-
45
- let(:runner_class) do
46
- Class.new(worker_class) {
47
- attr_accessor :stopped
48
- def run()
49
- super()
50
- stopped = false
51
- loop do
52
- break if stopped
53
- Thread.pass
54
- end
55
- end
56
- def stop() super(); stopped = true; end
57
- }
58
- end
59
-
60
- let(:worker){ worker_class.new }
61
-
62
- subject{ Supervisor.new(strategy: :one_for_one, monitor_interval: 0.1) }
63
-
64
- it_should_behave_like :runnable
65
-
66
- after(:each) do
67
- subject.stop
68
- kill_rogue_threads
69
- sleep(0.1)
70
- end
71
-
72
- context '#initialize' do
73
-
74
- it 'sets the initial length to zero' do
75
- supervisor = Supervisor.new
76
- supervisor.length.should == 0
77
- end
78
-
79
- it 'sets the initial length to one when a worker is provided' do
80
- supervisor = Supervisor.new(worker: worker)
81
- supervisor.length.should == 1
82
- end
83
-
84
- it 'sets the initial state to stopped' do
85
- supervisor = Supervisor.new
86
- supervisor.should_not be_running
87
- end
88
-
89
- it 'uses the given monitor interval' do
90
- supervisor = Supervisor.new
91
- supervisor.monitor_interval.should == Supervisor::DEFAULT_MONITOR_INTERVAL
92
- end
93
-
94
- it 'uses the default monitor interval when none given' do
95
- supervisor = Supervisor.new(monitor_interval: 5)
96
- supervisor.monitor_interval.should == 5
97
- end
98
-
99
- it 'raises an exception when given an invalid monitor interval' do
100
- lambda {
101
- Supervisor.new(monitor_interval: -1)
102
- }.should raise_error(ArgumentError)
103
-
104
- lambda {
105
- Supervisor.new(monitor_interval: 'bogus')
106
- }.should raise_error(ArgumentError)
107
- end
108
-
109
- it 'uses the given restart strategy' do
110
- supervisor = Supervisor.new(restart_strategy: :rest_for_one)
111
- supervisor.restart_strategy.should eq :rest_for_one
112
- end
113
-
114
- it 'uses :one_for_one when no restart strategy given' do
115
- supervisor = Supervisor.new
116
- supervisor.restart_strategy.should eq :one_for_one
117
- end
118
-
119
- it 'raises an exception when given an invalid restart strategy' do
120
- Supervisor::RESTART_STRATEGIES.each do |strategy|
121
- lambda {
122
- supervisor = Supervisor.new(strategy: strategy)
123
- }.should_not raise_error
124
- end
125
-
126
- lambda {
127
- supervisor = Supervisor.new(strategy: :bogus)
128
- }.should raise_error(ArgumentError)
129
- end
130
-
131
- it 'uses the given maximum restart value' do
132
- supervisor = Supervisor.new(max_restart: 3)
133
- supervisor.max_r.should == 3
134
-
135
- supervisor = Supervisor.new(max_r: 3)
136
- supervisor.max_restart.should == 3
137
- end
138
-
139
- it 'uses the default maximum restart value when none given' do
140
- supervisor = Supervisor.new
141
- supervisor.max_restart.should == Supervisor::DEFAULT_MAX_RESTART
142
- supervisor.max_r.should == Supervisor::DEFAULT_MAX_RESTART
143
- end
144
-
145
- it 'raises an exception when given an invalid maximum restart value' do
146
- lambda {
147
- Supervisor.new(max_restart: -1)
148
- }.should raise_error(ArgumentError)
149
-
150
- lambda {
151
- Supervisor.new(max_restart: 'bogus')
152
- }.should raise_error(ArgumentError)
153
- end
154
-
155
- it 'uses the given maximum time value' do
156
- supervisor = Supervisor.new(max_time: 3)
157
- supervisor.max_t.should == 3
158
-
159
- supervisor = Supervisor.new(max_t: 3)
160
- supervisor.max_time.should == 3
161
- end
162
-
163
- it 'uses the default maximum time value when none given' do
164
- supervisor = Supervisor.new
165
- supervisor.max_time.should == Supervisor::DEFAULT_MAX_TIME
166
- supervisor.max_t.should == Supervisor::DEFAULT_MAX_TIME
167
- end
168
-
169
- it 'raises an exception when given an invalid maximum time value' do
170
- lambda {
171
- Supervisor.new(max_time: -1)
172
- }.should raise_error(ArgumentError)
173
-
174
- lambda {
175
- Supervisor.new(max_time: 'bogus')
176
- }.should raise_error(ArgumentError)
177
- end
178
- end
179
-
180
- context '#run' do
181
-
182
- it 'runs the monitor' do
183
- subject.should_receive(:monitor).with(no_args()).at_least(1).times
184
- t = Thread.new{ subject.run }
185
- sleep(0.1)
186
- subject.stop
187
- Thread.kill(t) unless t.nil?
188
- end
189
-
190
- it 'calls #run on all workers' do
191
- supervisor = Supervisor.new(worker: worker)
192
- # must stub AFTER adding or else #add_worker will reject
193
- worker.should_receive(:run).with(no_args())
194
- t = Thread.new{ supervisor.run }
195
- sleep(0.1)
196
- supervisor.stop
197
- Thread.kill(t)
198
- end
199
-
200
- it 'sets the state to running' do
201
- t = Thread.new{ subject.run }
202
- sleep(0.1)
203
- subject.should be_running
204
- subject.stop
205
- Thread.kill(t)
206
- end
207
-
208
- it 'raises an exception when already running' do
209
- @thread = nil
210
- subject.run!
211
- lambda {
212
- @thread = Thread.new do
213
- Thread.current.abort_on_exception = true
214
- subject.run
215
- end
216
- sleep(0.1)
217
- }.should raise_error(StandardError)
218
- subject.stop
219
- Thread.kill(@thread) unless @thread.nil?
220
- end
221
- end
222
-
223
- context '#run!' do
224
-
225
- it 'runs the monitor thread' do
226
- thread = Thread.new{ nil }
227
- Thread.should_receive(:new).with(no_args()).and_return(thread)
228
- subject.run!
229
- sleep(0.1)
230
- end
231
-
232
- it 'calls #run on all workers' do
233
- supervisor = Supervisor.new(worker: worker)
234
- # must stub AFTER adding or else #add_worker will reject
235
- worker.should_receive(:run).with(no_args())
236
- supervisor.run!
237
- sleep(0.1)
238
- end
239
-
240
- it 'sets the state to running' do
241
- subject.run!
242
- subject.should be_running
243
- end
244
-
245
- it 'raises an exception when already running' do
246
- subject.run!
247
- lambda {
248
- subject.run!
249
- }.should raise_error(StandardError)
250
- end
251
- end
252
-
253
- context '#stop' do
254
-
255
- def mock_thread(status = 'run')
256
- thread = double('thread')
257
- thread.should_receive(:status).with(no_args()).and_return(status)
258
- thread.stub(:join).with(any_args()).and_return(thread)
259
- Thread.stub(:new).with(no_args()).and_return(thread)
260
- return thread
261
- end
262
-
263
- it 'wakes the monitor thread if sleeping' do
264
- thread = mock_thread('sleep')
265
- thread.should_receive(:run).once.with(no_args())
266
-
267
- subject.run!
268
- sleep(0.1)
269
- subject.stop
270
- end
271
-
272
- it 'kills the monitor thread if it does not wake up' do
273
- thread = mock_thread('run')
274
- thread.should_receive(:join).with(any_args()).and_return(nil)
275
- thread.should_receive(:kill).with(no_args())
276
-
277
- subject.run!
278
- sleep(0.1)
279
- subject.stop
280
- end
281
-
282
- it 'calls #stop on all workers' do
283
- workers = (1..3).collect{ runner_class.new }
284
- workers.each{|worker| subject.add_worker(worker)}
285
- # must stub AFTER adding or else #add_worker will reject
286
- workers.each{|worker| worker.should_receive(:stop).with(no_args())}
287
- subject.run!
288
- sleep(0.1)
289
- subject.stop
290
- end
291
-
292
- it 'sets the state to stopped' do
293
- subject.run!
294
- subject.stop
295
- subject.should_not be_running
296
- end
297
-
298
- it 'returns true immediately when already stopped' do
299
- subject.stop.should be_true
300
- end
301
-
302
- it 'unblocks a thread blocked by #run and exits normally' do
303
- supervisor = Supervisor.new(monitor_interval: 0.1)
304
- @thread = Thread.new{ sleep(0.5); supervisor.stop }
305
- sleep(0.1)
306
- lambda {
307
- Concurrent::timeout(1){ supervisor.run }
308
- }.should_not raise_error
309
- Thread.kill(@thread) unless @thread.nil?
310
- end
311
- end
312
-
313
- context '#running?' do
314
-
315
- it 'returns true when running' do
316
- subject.run!
317
- subject.should be_running
318
- end
319
-
320
- it 'returns false when stopped' do
321
- subject.run!
322
- subject.stop
323
- subject.should_not be_running
324
- end
325
- end
326
-
327
- context '#length' do
328
-
329
- it 'returns a count of attached workers' do
330
- workers = (1..3).collect{ worker.dup }
331
- workers.each{|worker| subject.add_worker(worker)}
332
- subject.length.should == 3
333
- end
334
- end
335
-
336
- context '#count' do
337
-
338
- let(:stoppers){ Array.new }
339
-
340
- let(:busy_supervisor) do
341
- supervisor = Supervisor.new(monitor_interval: 60)
342
- 3.times do
343
- supervisor.add_worker(sleeper_class.new)
344
- supervisor.add_worker(error_class.new)
345
- supervisor.add_worker(runner_class.new)
346
-
347
- stopper = stopper_class.new
348
- stoppers << stopper
349
- supervisor.add_worker(stopper)
350
- end
351
- supervisor
352
- end
353
-
354
- let!(:total_count){ 12 }
355
- let!(:active_count){ 6 }
356
- let!(:sleeping_count){ 3 }
357
- let!(:running_count){ 3 }
358
- let!(:aborting_count){ 3 }
359
- let!(:stopped_count){ 3 }
360
- let!(:abend_count){ 3 }
361
-
362
- after(:each) do
363
- busy_supervisor.stop
364
- end
365
-
366
- it 'returns an immutable WorkerCounts object' do
367
- counts = subject.count
368
- counts.should be_a(Supervisor::WorkerCounts)
369
-
370
- lambda {
371
- counts.specs += 1
372
- }.should raise_error(RuntimeError)
373
- end
374
-
375
- it 'returns the total worker count as #specs' do
376
- subject.count.specs.should eq 0
377
-
378
- 3.times do
379
- subject.add_worker(worker_class.new, type: :worker)
380
- subject.add_worker(worker_class.new, type: :supervisor)
381
- end
382
-
383
- subject.count.specs.should eq 6
384
- end
385
-
386
- it 'returns the count of all children marked as :supervisor as #supervisors' do
387
- subject.count.supervisors.should eq 0
388
-
389
- 3.times do
390
- subject.add_worker(worker_class.new, type: :worker)
391
- subject.add_worker(worker_class.new, type: :supervisor)
392
- end
393
-
394
- subject.count.supervisors.should eq 3
395
- end
396
-
397
- it 'returns the count of all children marked as :worker as #workers' do
398
- subject.count.workers.should eq 0
399
-
400
- 3.times do
401
- subject.add_worker(worker_class.new, type: :worker)
402
- subject.add_worker(worker_class.new, type: :supervisor)
403
- end
404
-
405
- subject.count.workers.should eq 3
406
- end
407
-
408
- it 'returns the count of all active workers as #active' do
409
- busy_supervisor.count.active.should eq 0
410
- busy_supervisor.run!
411
- sleep(0.5)
412
-
413
- busy_supervisor.count.active.should eq active_count
414
- end
415
-
416
- it 'returns the count of all sleeping workers as #sleeping' do
417
- busy_supervisor.count.sleeping.should eq 0
418
- busy_supervisor.run!
419
- sleep(0.5)
420
-
421
- busy_supervisor.count.sleeping.should eq sleeping_count
422
- end
423
-
424
- it 'returns the count of all running workers as #running' do
425
- busy_supervisor.count.running.should eq 0
426
- busy_supervisor.run!
427
- sleep(0.5)
428
-
429
- busy_supervisor.count.running.should eq running_count
430
- end
431
-
432
- it 'returns the count of all aborting workers as #aborting' do
433
- busy_supervisor.count.aborting.should eq 0
434
-
435
- count = Supervisor::WorkerCounts.new(5, 0, 5)
436
- count.status = %w[aborting run aborting false aborting]
437
- count.aborting.should eq 3
438
- end
439
-
440
- it 'returns the count of all stopped workers as #stopped' do
441
- busy_supervisor.count.stopped.should eq total_count
442
- busy_supervisor.run!
443
- stoppers.each{|stopper| stopper.latch.wait(1) }
444
- sleep(0.1)
445
-
446
- busy_supervisor.count.stopped.should eq stopped_count
447
- end
448
-
449
- it 'returns the count of all workers terminated by exception as #abend' do
450
- busy_supervisor.count.abend.should eq 0
451
- busy_supervisor.run!
452
- stoppers.each{|stopper| stopper.latch.wait(1) }
453
- sleep(0.1)
454
-
455
- busy_supervisor.count.abend.should eq abend_count
456
- end
457
- end
458
-
459
- context '#current_restart_count' do
460
-
461
- it 'is zero for a new Supervisor' do
462
- subject.current_restart_count.should eq 0
463
- end
464
-
465
- it 'returns the number of worker restarts' do
466
- worker = error_class.new
467
- supervisor = Supervisor.new(monitor_interval: 0.1)
468
- supervisor.add_worker(worker)
469
- supervisor.run!
470
- sleep(0.3)
471
- supervisor.current_restart_count.should > 0
472
- supervisor.stop
473
- end
474
-
475
- it 'resets to zero on #stop' do
476
- worker = error_class.new
477
- supervisor = Supervisor.new(monitor_interval: 0.1)
478
- supervisor.add_worker(worker)
479
- supervisor.run!
480
- sleep(0.3)
481
- supervisor.stop
482
- sleep(0.1)
483
- supervisor.current_restart_count.should eq 0
484
- end
485
- end
486
-
487
- context '#add_worker' do
488
-
489
- it 'adds the worker when stopped' do
490
- subject.add_worker(worker)
491
- subject.length.should == 1
492
- end
493
-
494
- it 'runs the worker when the supervisor is running' do
495
- worker = worker_class.new
496
- worker.start_count.to_i.should eq 0
497
- subject.run!
498
- sleep(0.1)
499
- subject.add_worker(worker).should be_true
500
- sleep(0.1)
501
- worker.start_count.should >= 1
502
- end
503
-
504
- it 'rejects a worker without the :runnable behavior' do
505
- subject.add_worker('bogus worker')
506
- subject.length.should == 0
507
- end
508
-
509
- it 'sets the restart type to the given value' do
510
- subject.add_worker(worker_class.new, restart: :temporary)
511
- worker = subject.instance_variable_get(:@workers).first
512
- worker.restart.should eq :temporary
513
- end
514
-
515
- it 'sets the restart type to :permanent when none given' do
516
- subject.add_worker(worker_class.new)
517
- worker = subject.instance_variable_get(:@workers).first
518
- worker.restart.should eq :permanent
519
- end
520
-
521
- it 'raises an exception when given an invalid restart type' do
522
- lambda {
523
- subject.add_worker(worker_class.new, restart: :bogus)
524
- }.should raise_error(ArgumentError)
525
- end
526
-
527
- it 'sets the child type to the given value' do
528
- subject.add_worker(worker_class.new, type: :supervisor)
529
- worker = subject.instance_variable_get(:@workers).first
530
- worker.type.should eq :supervisor
531
- end
532
-
533
- it 'sets the worker type to :worker when none given' do
534
- subject.add_worker(worker_class.new)
535
- worker = subject.instance_variable_get(:@workers).first
536
- worker.type.should eq :worker
537
- end
538
-
539
- it 'sets the worker type to :supervisor when #is_a? Supervisor' do
540
- subject.add_worker(Supervisor.new)
541
- worker = subject.instance_variable_get(:@workers).first
542
- worker.type.should eq :supervisor
543
- end
544
-
545
- it 'raises an exception when given an invalid restart type' do
546
- lambda {
547
- subject.add_worker(worker_class.new, type: :bogus)
548
- }.should raise_error(ArgumentError)
549
- end
550
-
551
- it 'returns an object id when a worker is accepted' do
552
- worker_id = subject.add_worker(worker)
553
- worker_id.should be_a(Integer)
554
- first = subject.instance_variable_get(:@workers).first
555
- worker_id.should eq first.object_id
556
- end
557
-
558
- it 'returns nil when a worker is not accepted' do
559
- subject.add_worker('bogus worker').should be_nil
560
- end
561
- end
562
-
563
- context '#add_workers' do
564
-
565
- it 'calls #add_worker once for each worker' do
566
- workers = 5.times.collect{ worker_class.new }
567
- workers.each do |worker|
568
- subject.should_receive(:add_worker).once.with(worker, anything())
569
- end
570
- subject.add_workers(workers)
571
- end
572
-
573
- it 'passes the options hash to each #add_worker call' do
574
- options = {
575
- restart: :permanent,
576
- type: :worker
577
- }
578
- workers = 5.times.collect{ worker_class.new }
579
- workers.each do |worker|
580
- subject.should_receive(:add_worker).once.with(anything(), options)
581
- end
582
- subject.add_workers(workers, options)
583
- end
584
-
585
- it 'returns an array of object identifiers' do
586
- workers = 5.times.collect{ worker_class.new }
587
- context = subject.add_workers(workers)
588
- context.size.should == 5
589
- context.each do |wc|
590
- wc.should be_a(Fixnum)
591
- end
592
- end
593
- end
594
-
595
- context '#remove_worker' do
596
-
597
- it 'returns false if the worker is running' do
598
- id = subject.add_worker(sleeper_class.new)
599
- subject.run!
600
- sleep(0.1)
601
- subject.remove_worker(id).should be_false
602
- end
603
-
604
- it 'returns nil if the worker is not found' do
605
- subject.run!
606
- sleep(0.1)
607
- subject.remove_worker(1234).should be_nil
608
- end
609
-
610
- it 'returns the worker on success' do
611
- worker = error_class.new
612
- supervisor = Supervisor.new(monitor_interval: 60)
613
- id = supervisor.add_worker(worker)
614
- supervisor.run!
615
- sleep(0.1)
616
- supervisor.remove_worker(id).should eq worker
617
- supervisor.stop
618
- end
619
-
620
- it 'removes the worker from the supervisor on success' do
621
- worker = error_class.new
622
- supervisor = Supervisor.new(monitor_interval: 60)
623
- id = supervisor.add_worker(worker)
624
- supervisor.length.should == 1
625
- supervisor.run!
626
- sleep(0.1)
627
- supervisor.remove_worker(id)
628
- supervisor.length.should == 0
629
- supervisor.stop
630
- end
631
- end
632
-
633
- context '#stop_worker' do
634
-
635
- it 'returns true if the supervisor is not running' do
636
- worker = worker_class.new
637
- id = subject.add_worker(worker)
638
- subject.stop_worker(id).should be_true
639
- end
640
-
641
- it 'returns nil if the worker is not found' do
642
- worker = sleeper_class.new
643
- id = subject.add_worker(worker)
644
- subject.run!
645
- sleep(0.1)
646
- subject.stop_worker(1234).should be_nil
647
- end
648
-
649
- it 'returns true on success' do
650
- worker = sleeper_class.new
651
- id = subject.add_worker(worker)
652
- subject.run!
653
- sleep(0.1)
654
- worker.should_receive(:stop).at_least(1).times.and_return(true)
655
- subject.stop_worker(id).should be_true
656
- end
657
-
658
- it 'deletes the worker if it is :temporary' do
659
- worker = sleeper_class.new
660
- id = subject.add_worker(worker, restart: :temporary)
661
- subject.size.should eq 1
662
- subject.run!
663
- sleep(0.1)
664
- subject.stop_worker(id).should be_true
665
- subject.size.should eq 0
666
- end
667
-
668
- it 'does not implicitly restart the worker' do
669
- supervisor = Supervisor.new(monitor_interval: 0.1)
670
- worker = runner_class.new
671
- id = supervisor.add_worker(worker, restart: :permanent)
672
- supervisor.run!
673
- sleep(0.1)
674
- supervisor.stop_worker(id)
675
- sleep(0.5)
676
- supervisor.stop
677
- worker.start_count.should eq 1
678
- end
679
- end
680
-
681
- context '#start_worker' do
682
-
683
- it 'returns false if the supervisor is not running' do
684
- worker = worker_class.new
685
- id = subject.add_worker(worker)
686
- subject.start_worker(id).should be_false
687
- end
688
-
689
- it 'returns nil if the worker is not found' do
690
- subject.run!
691
- sleep(0.1)
692
- subject.start_worker(1234).should be_nil
693
- end
694
-
695
- it 'starts the worker if not running' do
696
- supervisor = Supervisor.new(monitor_interval: 60)
697
- worker = error_class.new
698
- id = supervisor.add_worker(worker)
699
- supervisor.run!
700
- sleep(0.1)
701
- supervisor.start_worker(id)
702
- sleep(0.1)
703
- worker.start_count.should == 2
704
- supervisor.stop
705
- end
706
-
707
- it 'returns true when the worker is successfully started' do
708
- supervisor = Supervisor.new(monitor_interval: 60)
709
- worker = error_class.new
710
- id = supervisor.add_worker(worker)
711
- supervisor.run!
712
- sleep(0.1)
713
- supervisor.start_worker(id).should be_true
714
- supervisor.stop
715
- end
716
-
717
- it 'returns true if the worker was already running' do
718
- supervisor = Supervisor.new(monitor_interval: 60)
719
- worker = sleeper_class.new
720
- id = supervisor.add_worker(worker)
721
- supervisor.run!
722
- sleep(0.1)
723
- supervisor.start_worker(id).should be_true
724
- worker.start_count.should == 1
725
- supervisor.stop
726
- end
727
- end
728
-
729
- context '#restart_worker' do
730
-
731
- it 'returns false if the supervisor is not running' do
732
- worker = worker_class.new
733
- id = subject.add_worker(worker)
734
- subject.restart_worker(id).should be_false
735
- end
736
-
737
- it 'returns nil if the worker is not found' do
738
- subject.run!
739
- sleep(0.1)
740
- subject.restart_worker(1234).should be_nil
741
- end
742
-
743
- it 'returns false if the worker is :temporary' do
744
- worker = worker_class.new
745
- id = subject.add_worker(worker, restart: :temporary)
746
- subject.run!
747
- sleep(0.1)
748
- subject.restart_worker(id).should be_false
749
- end
750
-
751
- it 'stops and then starts a worker that is running' do
752
- worker = runner_class.new
753
- id = subject.add_worker(worker)
754
- subject.run!
755
- sleep(0.1)
756
- subject.restart_worker(id)
757
- sleep(0.1)
758
- worker.start_count.should == 2
759
- worker.stop_count.should == 1
760
- end
761
-
762
- it 'returns true if the worker is running and is successfully restarted' do
763
- worker = runner_class.new
764
- id = subject.add_worker(worker)
765
- subject.run!
766
- sleep(0.1)
767
- subject.restart_worker(id).should be_true
768
- end
769
-
770
- it 'starts a worker that is not running' do
771
- worker = error_class.new
772
- id = subject.add_worker(worker)
773
- subject.run!
774
- sleep(0.1)
775
- subject.restart_worker(id)
776
- sleep(0.1)
777
- worker.start_count.should >= 2
778
- end
779
-
780
- it 'returns true if the worker is not running and is successfully started' do
781
- worker = error_class.new
782
- id = subject.add_worker(worker)
783
- subject.run!
784
- sleep(0.1)
785
- subject.restart_worker(id).should be_true
786
- end
787
- end
788
-
789
- context 'maximum restart frequency' do
790
-
791
- context '#exceeded_max_restart_frequency?' do
792
-
793
- # Normally I am very opposed to testing private methods
794
- # but this functionality has proven extremely difficult to test.
795
- # Geting the timing right is almost impossible. This is the
796
- # best approach I could think of.
797
-
798
- it 'increments the restart count on every call' do
799
- subject.send(:exceeded_max_restart_frequency?)
800
- subject.current_restart_count.should eq 1
801
-
802
- subject.send(:exceeded_max_restart_frequency?)
803
- subject.current_restart_count.should eq 2
804
-
805
- subject.send(:exceeded_max_restart_frequency?)
806
- subject.current_restart_count.should eq 3
807
- end
808
-
809
- it 'returns false when the restart count is lower than :max_restart' do
810
- supervisor = Supervisor.new(max_restart: 5, max_time: 60)
811
-
812
- Timecop.freeze do
813
- 4.times do
814
- Timecop.travel(5)
815
- supervisor.send(:exceeded_max_restart_frequency?).should be_false
816
- end
817
-
818
- Timecop.travel(5)
819
- supervisor.send(:exceeded_max_restart_frequency?).should be_true
820
- end
821
- end
822
-
823
- it 'returns false when the restart count is high but the time range is out of scope' do
824
- supervisor = Supervisor.new(max_restart: 3, max_time: 8)
825
-
826
- Timecop.freeze do
827
- 10.times do
828
- Timecop.travel(5)
829
- supervisor.send(:exceeded_max_restart_frequency?).should be_false
830
- end
831
- end
832
- end
833
-
834
- it 'returns true when the restart count is exceeded within the max time range' do
835
- supervisor = Supervisor.new(max_restart: 2, max_time: 5)
836
- Timecop.freeze do
837
- supervisor.send(:exceeded_max_restart_frequency?).should be_false
838
- Timecop.travel(1)
839
- supervisor.send(:exceeded_max_restart_frequency?).should be_true
840
- end
841
- end
842
- end
843
-
844
- context 'restarts when true for strategy' do
845
-
846
- specify ':one_for_one' do
847
- supervisor = Supervisor.new(restart_strategy: :one_for_one,
848
- monitor_interval: 0.1)
849
- supervisor.add_worker(error_class.new)
850
- supervisor.stub(:exceeded_max_restart_frequency?).once.and_return(true)
851
- future = Concurrent::Future.execute{ supervisor.run }
852
- future.value(1)
853
- supervisor.should_not be_running
854
- end
855
-
856
- specify ':one_for_all' do
857
- supervisor = Supervisor.new(restart_strategy: :one_for_all,
858
- monitor_interval: 0.1)
859
- supervisor.add_worker(error_class.new)
860
- supervisor.should_receive(:exceeded_max_restart_frequency?).once.and_return(true)
861
- future = Concurrent::Future.execute{ supervisor.run }
862
- future.value(1)
863
- supervisor.should_not be_running
864
- end
865
-
866
- specify ':rest_for_one' do
867
- supervisor = Supervisor.new(restart_strategy: :rest_for_one,
868
- monitor_interval: 0.1)
869
- supervisor.add_worker(error_class.new)
870
- supervisor.should_receive(:exceeded_max_restart_frequency?).once.and_return(true)
871
- future = Concurrent::Future.execute{ supervisor.run }
872
- future.value(1)
873
- supervisor.should_not be_running
874
- end
875
- end
876
- end
877
-
878
- context 'restart strategies' do
879
-
880
- context ':one_for_one' do
881
-
882
- it 'restarts any worker that stops' do
883
-
884
- workers = [
885
- sleeper_class.new,
886
- stopper_class.new,
887
- sleeper_class.new
888
- ]
889
-
890
- supervisor = Supervisor.new(strategy: :one_for_one, monitor_interval: 0.1)
891
- workers.each{|worker| supervisor.add_worker(worker) }
892
-
893
- supervisor.run!
894
- sleep(1)
895
-
896
- workers[0].start_count.should == 1
897
- workers[1].start_count.should >= 2
898
- workers[2].start_count.should == 1
899
-
900
- supervisor.stop
901
- end
902
-
903
- it 'restarts any dead threads' do
904
-
905
- workers = [
906
- sleeper_class.new,
907
- error_class.new,
908
- sleeper_class.new
909
- ]
910
-
911
- supervisor = Supervisor.new(strategy: :one_for_one, monitor_interval: 0.1)
912
- workers.each{|worker| supervisor.add_worker(worker) }
913
-
914
- supervisor.run!
915
- sleep(1)
916
-
917
- workers[0].start_count.should == 1
918
- workers[1].start_count.should >= 2
919
- workers[2].start_count.should == 1
920
-
921
- supervisor.stop
922
- end
923
- end
924
-
925
- context ':one_for_all' do
926
-
927
- it 'restarts all workers when one stops' do
928
-
929
- workers = [
930
- sleeper_class.new,
931
- stopper_class.new,
932
- sleeper_class.new
933
- ]
934
-
935
- supervisor = Supervisor.new(strategy: :one_for_all, monitor_interval: 0.1)
936
- workers.each{|worker| supervisor.add_worker(worker) }
937
-
938
- workers[0].should_receive(:stop).once.with(no_args())
939
- workers[2].should_receive(:stop).once.with(no_args())
940
-
941
- supervisor.run!
942
- sleep(1)
943
- workers.each{|worker| worker.start_count.should >= 2 }
944
-
945
- supervisor.stop
946
- end
947
-
948
- it 'restarts all workers when one thread dies' do
949
-
950
- workers = [
951
- sleeper_class.new,
952
- error_class.new,
953
- sleeper_class.new
954
- ]
955
-
956
- supervisor = Supervisor.new(strategy: :one_for_all, monitor_interval: 0.1)
957
- workers.each{|worker| supervisor.add_worker(worker) }
958
-
959
- workers[0].should_receive(:stop).once.with(no_args())
960
- workers[2].should_receive(:stop).once.with(no_args())
961
-
962
- supervisor.run!
963
- sleep(1)
964
- workers.each{|worker| worker.start_count.should >= 2 }
965
-
966
- supervisor.stop
967
- end
968
- end
969
-
970
- context ':rest_for_one' do
971
-
972
- it 'restarts a stopped worker and all workers added after it' do
973
-
974
- workers = [
975
- sleeper_class.new,
976
- stopper_class.new,
977
- sleeper_class.new
978
- ]
979
-
980
- supervisor = Supervisor.new(strategy: :rest_for_one, monitor_interval: 0.1)
981
- workers.each{|worker| supervisor.add_worker(worker) }
982
-
983
- workers[0].should_not_receive(:stop)
984
- workers[2].should_receive(:stop).once.with(no_args())
985
-
986
- supervisor.run!
987
- sleep(1)
988
-
989
- workers[0].start_count.should == 1
990
- workers[1].start_count.should >= 2
991
- workers[2].start_count.should >= 2
992
-
993
- supervisor.stop
994
- end
995
-
996
- it 'restarts a dead worker thread and all workers added after it' do
997
-
998
- workers = [
999
- sleeper_class.new,
1000
- error_class.new,
1001
- sleeper_class.new
1002
- ]
1003
-
1004
- supervisor = Supervisor.new(strategy: :rest_for_one, monitor_interval: 0.1)
1005
- workers.each{|worker| supervisor.add_worker(worker) }
1006
-
1007
- workers[0].should_not_receive(:stop)
1008
- workers[2].should_receive(:stop).once.with(no_args())
1009
-
1010
- supervisor.run!
1011
- sleep(1)
1012
-
1013
- workers[0].start_count.should == 1
1014
- workers[1].start_count.should >= 2
1015
- workers[2].start_count.should >= 2
1016
-
1017
- supervisor.stop
1018
- end
1019
- end
1020
- end
1021
-
1022
- context 'child restart options' do
1023
-
1024
- def worker_status(supervisor)
1025
- worker = supervisor.instance_variable_get(:@workers).first
1026
- return worker.thread.status
1027
- end
1028
-
1029
- specify ':permanent restarts on abend' do
1030
- worker = error_class.new
1031
- supervisor = Supervisor.new(monitor_interval: 0.1)
1032
- supervisor.add_worker(worker, restart: :permanent)
1033
-
1034
- supervisor.run!
1035
- sleep(0.5)
1036
- supervisor.stop
1037
-
1038
- worker.start_count.should >= 1
1039
- end
1040
-
1041
- specify ':permanent restarts on normal stop' do
1042
- worker = stopper_class.new(0.1)
1043
- supervisor = Supervisor.new(monitor_interval: 0.1)
1044
- supervisor.add_worker(worker, restart: :permanent)
1045
-
1046
- supervisor.run!
1047
- sleep(0.5)
1048
- supervisor.stop
1049
-
1050
- worker.start_count.should >= 1
1051
- end
1052
-
1053
- specify ':temporary does not restart on abend' do
1054
- worker = error_class.new
1055
- supervisor = Supervisor.new(monitor_interval: 0.1)
1056
- supervisor.add_worker(worker, restart: :temporary)
1057
-
1058
- supervisor.run!
1059
- sleep(0.5)
1060
- supervisor.stop
1061
-
1062
- worker.start_count.should eq 1
1063
- end
1064
-
1065
- specify ':temporary does not restart on normal stop' do
1066
- worker = stopper_class.new
1067
- supervisor = Supervisor.new(monitor_interval: 0.1)
1068
- supervisor.add_worker(worker, restart: :temporary)
1069
-
1070
- supervisor.run!
1071
- sleep(0.5)
1072
- supervisor.stop
1073
-
1074
- worker.start_count.should eq 1
1075
- end
1076
-
1077
- specify ':temporary is deleted on abend' do
1078
- worker = error_class.new
1079
- supervisor = Supervisor.new(monitor_interval: 0.1)
1080
- supervisor.add_worker(worker, restart: :temporary)
1081
-
1082
- supervisor.run!
1083
- sleep(0.5)
1084
- supervisor.stop
1085
-
1086
- supervisor.size.should eq 0
1087
- end
1088
-
1089
- specify ':temporary is deleted on normal stop' do
1090
- worker = stopper_class.new
1091
- supervisor = Supervisor.new(monitor_interval: 0.1)
1092
- supervisor.add_worker(worker, restart: :temporary)
1093
-
1094
- supervisor.run!
1095
- sleep(0.5)
1096
- supervisor.stop
1097
-
1098
- supervisor.size.should eq 0
1099
- end
1100
-
1101
- specify ':transient restarts on abend' do
1102
- worker = error_class.new
1103
- supervisor = Supervisor.new(monitor_interval: 0.1)
1104
- supervisor.add_worker(worker, restart: :transient)
1105
-
1106
- supervisor.run!
1107
- sleep(0.5)
1108
- supervisor.stop
1109
-
1110
- worker.start_count.should >= 1
1111
- end
1112
-
1113
- specify ':transient does not restart on normal stop' do
1114
- worker = stopper_class.new
1115
- supervisor = Supervisor.new(monitor_interval: 0.1)
1116
- supervisor.add_worker(worker, restart: :transient)
1117
-
1118
- supervisor.run!
1119
- sleep(0.5)
1120
- supervisor.stop
1121
-
1122
- worker.start_count.should eq 1
1123
- end
1124
- end
1125
-
1126
- context 'supervision tree' do
1127
-
1128
- specify do
1129
- s1 = Supervisor.new(monitor_interval: 0.1)
1130
- s2 = Supervisor.new(monitor_interval: 0.1)
1131
- s3 = Supervisor.new(monitor_interval: 0.1)
1132
-
1133
- workers = (1..3).collect{ sleeper_class.new }
1134
- workers.each{|worker| s3.add_worker(worker)}
1135
-
1136
- workers.each{|worker| worker.should_receive(:stop).at_least(1).times.with(no_args())}
1137
-
1138
- s1.add_worker(s2)
1139
- s2.add_worker(s3)
1140
-
1141
- s1.run!
1142
- sleep(0.2)
1143
-
1144
- s1.stop
1145
- sleep(0.2)
1146
- end
1147
- end
1148
- end
1149
- end