concurrent-ruby 0.6.0.pre.1 → 0.6.0.pre.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +16 -0
  3. data/lib/concurrent.rb +9 -29
  4. data/lib/concurrent/{actor.rb → actor/actor.rb} +3 -3
  5. data/lib/concurrent/actor/actor_context.rb +77 -0
  6. data/lib/concurrent/actor/actor_ref.rb +67 -0
  7. data/lib/concurrent/{postable.rb → actor/postable.rb} +1 -1
  8. data/lib/concurrent/actor/simple_actor_ref.rb +94 -0
  9. data/lib/concurrent/actors.rb +5 -0
  10. data/lib/concurrent/agent.rb +81 -47
  11. data/lib/concurrent/async.rb +35 -35
  12. data/lib/concurrent/atomic/atomic_boolean.rb +157 -0
  13. data/lib/concurrent/atomic/atomic_fixnum.rb +170 -0
  14. data/lib/concurrent/{condition.rb → atomic/condition.rb} +0 -0
  15. data/lib/concurrent/{copy_on_notify_observer_set.rb → atomic/copy_on_notify_observer_set.rb} +48 -13
  16. data/lib/concurrent/{copy_on_write_observer_set.rb → atomic/copy_on_write_observer_set.rb} +41 -20
  17. data/lib/concurrent/atomic/count_down_latch.rb +116 -0
  18. data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
  19. data/lib/concurrent/atomic/event.rb +103 -0
  20. data/lib/concurrent/{thread_local_var.rb → atomic/thread_local_var.rb} +0 -0
  21. data/lib/concurrent/atomics.rb +9 -0
  22. data/lib/concurrent/channel/buffered_channel.rb +6 -4
  23. data/lib/concurrent/channel/channel.rb +30 -2
  24. data/lib/concurrent/channel/unbuffered_channel.rb +2 -2
  25. data/lib/concurrent/channel/waitable_list.rb +3 -1
  26. data/lib/concurrent/channels.rb +5 -0
  27. data/lib/concurrent/{channel → collection}/blocking_ring_buffer.rb +16 -5
  28. data/lib/concurrent/collection/priority_queue.rb +305 -0
  29. data/lib/concurrent/{channel → collection}/ring_buffer.rb +6 -1
  30. data/lib/concurrent/collections.rb +3 -0
  31. data/lib/concurrent/configuration.rb +68 -19
  32. data/lib/concurrent/dataflow.rb +9 -9
  33. data/lib/concurrent/delay.rb +21 -13
  34. data/lib/concurrent/dereferenceable.rb +40 -33
  35. data/lib/concurrent/exchanger.rb +3 -0
  36. data/lib/concurrent/{cached_thread_pool.rb → executor/cached_thread_pool.rb} +8 -9
  37. data/lib/concurrent/executor/executor.rb +222 -0
  38. data/lib/concurrent/{fixed_thread_pool.rb → executor/fixed_thread_pool.rb} +6 -7
  39. data/lib/concurrent/{immediate_executor.rb → executor/immediate_executor.rb} +5 -5
  40. data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
  41. data/lib/concurrent/{java_fixed_thread_pool.rb → executor/java_fixed_thread_pool.rb} +7 -11
  42. data/lib/concurrent/executor/java_single_thread_executor.rb +21 -0
  43. data/lib/concurrent/{java_thread_pool_executor.rb → executor/java_thread_pool_executor.rb} +66 -77
  44. data/lib/concurrent/executor/one_by_one.rb +65 -0
  45. data/lib/concurrent/{per_thread_executor.rb → executor/per_thread_executor.rb} +4 -4
  46. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
  47. data/lib/concurrent/{ruby_fixed_thread_pool.rb → executor/ruby_fixed_thread_pool.rb} +5 -4
  48. data/lib/concurrent/executor/ruby_single_thread_executor.rb +72 -0
  49. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +282 -0
  50. data/lib/concurrent/{ruby_thread_pool_worker.rb → executor/ruby_thread_pool_worker.rb} +6 -6
  51. data/lib/concurrent/{safe_task_executor.rb → executor/safe_task_executor.rb} +20 -13
  52. data/lib/concurrent/executor/single_thread_executor.rb +35 -0
  53. data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
  54. data/lib/concurrent/executor/timer_set.rb +138 -0
  55. data/lib/concurrent/executors.rb +9 -0
  56. data/lib/concurrent/future.rb +39 -40
  57. data/lib/concurrent/ivar.rb +22 -15
  58. data/lib/concurrent/mvar.rb +2 -1
  59. data/lib/concurrent/obligation.rb +9 -3
  60. data/lib/concurrent/observable.rb +33 -0
  61. data/lib/concurrent/options_parser.rb +46 -0
  62. data/lib/concurrent/promise.rb +23 -24
  63. data/lib/concurrent/scheduled_task.rb +21 -45
  64. data/lib/concurrent/timer_task.rb +204 -126
  65. data/lib/concurrent/tvar.rb +1 -1
  66. data/lib/concurrent/utilities.rb +3 -36
  67. data/lib/concurrent/{processor_count.rb → utility/processor_count.rb} +1 -1
  68. data/lib/concurrent/utility/timeout.rb +36 -0
  69. data/lib/concurrent/utility/timer.rb +21 -0
  70. data/lib/concurrent/version.rb +1 -1
  71. data/lib/concurrent_ruby_ext.bundle +0 -0
  72. data/spec/concurrent/{actor_context_spec.rb → actor/actor_context_spec.rb} +0 -8
  73. data/spec/concurrent/{actor_ref_shared.rb → actor/actor_ref_shared.rb} +9 -59
  74. data/spec/concurrent/{actor_spec.rb → actor/actor_spec.rb} +43 -41
  75. data/spec/concurrent/{postable_shared.rb → actor/postable_shared.rb} +0 -0
  76. data/spec/concurrent/actor/simple_actor_ref_spec.rb +135 -0
  77. data/spec/concurrent/agent_spec.rb +160 -71
  78. data/spec/concurrent/atomic/atomic_boolean_spec.rb +172 -0
  79. data/spec/concurrent/atomic/atomic_fixnum_spec.rb +186 -0
  80. data/spec/concurrent/{condition_spec.rb → atomic/condition_spec.rb} +2 -2
  81. data/spec/concurrent/{copy_on_notify_observer_set_spec.rb → atomic/copy_on_notify_observer_set_spec.rb} +0 -0
  82. data/spec/concurrent/{copy_on_write_observer_set_spec.rb → atomic/copy_on_write_observer_set_spec.rb} +0 -0
  83. data/spec/concurrent/atomic/count_down_latch_spec.rb +151 -0
  84. data/spec/concurrent/atomic/cyclic_barrier_spec.rb +248 -0
  85. data/spec/concurrent/{event_spec.rb → atomic/event_spec.rb} +18 -3
  86. data/spec/concurrent/{observer_set_shared.rb → atomic/observer_set_shared.rb} +15 -6
  87. data/spec/concurrent/{thread_local_var_spec.rb → atomic/thread_local_var_spec.rb} +0 -0
  88. data/spec/concurrent/channel/buffered_channel_spec.rb +1 -1
  89. data/spec/concurrent/channel/channel_spec.rb +6 -4
  90. data/spec/concurrent/channel/probe_spec.rb +37 -9
  91. data/spec/concurrent/channel/unbuffered_channel_spec.rb +2 -2
  92. data/spec/concurrent/{channel → collection}/blocking_ring_buffer_spec.rb +0 -0
  93. data/spec/concurrent/collection/priority_queue_spec.rb +317 -0
  94. data/spec/concurrent/{channel → collection}/ring_buffer_spec.rb +0 -0
  95. data/spec/concurrent/configuration_spec.rb +4 -70
  96. data/spec/concurrent/dereferenceable_shared.rb +5 -4
  97. data/spec/concurrent/exchanger_spec.rb +10 -5
  98. data/spec/concurrent/{cached_thread_pool_shared.rb → executor/cached_thread_pool_shared.rb} +15 -37
  99. data/spec/concurrent/{fixed_thread_pool_shared.rb → executor/fixed_thread_pool_shared.rb} +0 -0
  100. data/spec/concurrent/{global_thread_pool_shared.rb → executor/global_thread_pool_shared.rb} +10 -8
  101. data/spec/concurrent/{immediate_executor_spec.rb → executor/immediate_executor_spec.rb} +0 -0
  102. data/spec/concurrent/{java_cached_thread_pool_spec.rb → executor/java_cached_thread_pool_spec.rb} +1 -21
  103. data/spec/concurrent/{java_fixed_thread_pool_spec.rb → executor/java_fixed_thread_pool_spec.rb} +0 -0
  104. data/spec/concurrent/executor/java_single_thread_executor_spec.rb +21 -0
  105. data/spec/concurrent/{java_thread_pool_executor_spec.rb → executor/java_thread_pool_executor_spec.rb} +0 -0
  106. data/spec/concurrent/{per_thread_executor_spec.rb → executor/per_thread_executor_spec.rb} +0 -4
  107. data/spec/concurrent/{ruby_cached_thread_pool_spec.rb → executor/ruby_cached_thread_pool_spec.rb} +1 -1
  108. data/spec/concurrent/{ruby_fixed_thread_pool_spec.rb → executor/ruby_fixed_thread_pool_spec.rb} +0 -0
  109. data/spec/concurrent/executor/ruby_single_thread_executor_spec.rb +18 -0
  110. data/spec/concurrent/{ruby_thread_pool_executor_spec.rb → executor/ruby_thread_pool_executor_spec.rb} +12 -24
  111. data/spec/concurrent/executor/safe_task_executor_spec.rb +103 -0
  112. data/spec/concurrent/{thread_pool_class_cast_spec.rb → executor/thread_pool_class_cast_spec.rb} +12 -0
  113. data/spec/concurrent/{thread_pool_executor_shared.rb → executor/thread_pool_executor_shared.rb} +0 -0
  114. data/spec/concurrent/{thread_pool_shared.rb → executor/thread_pool_shared.rb} +84 -119
  115. data/spec/concurrent/executor/timer_set_spec.rb +183 -0
  116. data/spec/concurrent/future_spec.rb +12 -0
  117. data/spec/concurrent/ivar_spec.rb +11 -1
  118. data/spec/concurrent/observable_shared.rb +173 -0
  119. data/spec/concurrent/observable_spec.rb +51 -0
  120. data/spec/concurrent/options_parser_spec.rb +71 -0
  121. data/spec/concurrent/runnable_shared.rb +6 -0
  122. data/spec/concurrent/scheduled_task_spec.rb +60 -40
  123. data/spec/concurrent/timer_task_spec.rb +130 -144
  124. data/spec/concurrent/{processor_count_spec.rb → utility/processor_count_spec.rb} +0 -0
  125. data/spec/concurrent/{utilities_spec.rb → utility/timeout_spec.rb} +0 -0
  126. data/spec/concurrent/utility/timer_spec.rb +52 -0
  127. metadata +147 -108
  128. data/lib/concurrent/actor_context.rb +0 -31
  129. data/lib/concurrent/actor_ref.rb +0 -39
  130. data/lib/concurrent/atomic.rb +0 -121
  131. data/lib/concurrent/channel/probe.rb +0 -19
  132. data/lib/concurrent/count_down_latch.rb +0 -60
  133. data/lib/concurrent/event.rb +0 -80
  134. data/lib/concurrent/java_cached_thread_pool.rb +0 -45
  135. data/lib/concurrent/ruby_cached_thread_pool.rb +0 -37
  136. data/lib/concurrent/ruby_thread_pool_executor.rb +0 -268
  137. data/lib/concurrent/simple_actor_ref.rb +0 -124
  138. data/lib/concurrent/thread_pool_executor.rb +0 -30
  139. data/spec/concurrent/atomic_spec.rb +0 -201
  140. data/spec/concurrent/count_down_latch_spec.rb +0 -125
  141. data/spec/concurrent/safe_task_executor_spec.rb +0 -58
  142. data/spec/concurrent/simple_actor_ref_spec.rb +0 -219
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe SafeTaskExecutor do
6
+
7
+ describe '#execute' do
8
+
9
+ context 'happy execution' do
10
+
11
+ let(:task) { Proc.new { 42 } }
12
+ subject { SafeTaskExecutor.new(task) }
13
+
14
+ it 'should return success' do
15
+ success, value, reason = subject.execute
16
+ success.should be_true
17
+ end
18
+
19
+ it 'should return task value' do
20
+ success, value, reason = subject.execute
21
+ value.should eq 42
22
+ end
23
+
24
+ it 'should return a nil reason' do
25
+ success, value, reason = subject.execute
26
+ reason.should be_nil
27
+ end
28
+
29
+ it 'passes all arguments to #execute to the task' do
30
+ expected = nil
31
+ task = proc {|*args| expected = args }
32
+ SafeTaskExecutor.new(task).execute(1, 2, 3)
33
+ expected.should eq [1, 2, 3]
34
+ end
35
+
36
+ it 'protectes #execute with a mutex' do
37
+ mutex = double(:mutex)
38
+ Mutex.should_receive(:new).with(no_args).and_return(mutex)
39
+ mutex.should_receive(:synchronize).with(no_args)
40
+ subject.execute
41
+ end
42
+ end
43
+
44
+ context 'failing execution' do
45
+
46
+ let(:task) { Proc.new { raise StandardError.new('an error') } }
47
+ subject { SafeTaskExecutor.new(task) }
48
+
49
+ it 'should return false success' do
50
+ success, value, reason = subject.execute
51
+ success.should be_false
52
+ end
53
+
54
+ it 'should return a nil value' do
55
+ success, value, reason = subject.execute
56
+ value.should be_nil
57
+ end
58
+
59
+ it 'should return the reason' do
60
+ success, value, reason = subject.execute
61
+ reason.should be_a(StandardError)
62
+ reason.message.should eq 'an error'
63
+ end
64
+
65
+ it 'rescues Exception when :rescue_exception is true' do
66
+ task = proc { raise Exception }
67
+ subject = SafeTaskExecutor.new(task, rescue_exception: true)
68
+ expect {
69
+ subject.execute
70
+ }.to_not raise_error
71
+ end
72
+
73
+ it 'rescues StandardError when :rescue_exception is false' do
74
+ task = proc { raise StandardError }
75
+ subject = SafeTaskExecutor.new(task, rescue_exception: false)
76
+ expect {
77
+ subject.execute
78
+ }.to_not raise_error
79
+
80
+ task = proc { raise Exception }
81
+ subject = SafeTaskExecutor.new(task, rescue_exception: false)
82
+ expect {
83
+ subject.execute
84
+ }.to raise_error(Exception)
85
+ end
86
+
87
+ it 'rescues StandardError by default' do
88
+ task = proc { raise StandardError }
89
+ subject = SafeTaskExecutor.new(task)
90
+ expect {
91
+ subject.execute
92
+ }.to_not raise_error
93
+
94
+ task = proc { raise Exception }
95
+ subject = SafeTaskExecutor.new(task)
96
+ expect {
97
+ subject.execute
98
+ }.to raise_error(Exception)
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -2,6 +2,18 @@ require 'spec_helper'
2
2
 
3
3
  module Concurrent
4
4
 
5
+ describe SingleThreadExecutor do
6
+ if jruby?
7
+ it 'inherits from JavaSingleThreadExecutor' do
8
+ SingleThreadExecutor.ancestors.should include(JavaSingleThreadExecutor)
9
+ end
10
+ else
11
+ it 'inherits from RubySingleThreadExecutor' do
12
+ SingleThreadExecutor.ancestors.should include(RubySingleThreadExecutor)
13
+ end
14
+ end
15
+ end
16
+
5
17
  describe ThreadPoolExecutor do
6
18
  if jruby?
7
19
  it 'inherits from JavaThreadPoolExecutor' do
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require_relative 'global_thread_pool_shared'
3
3
 
4
- share_examples_for :thread_pool do
4
+ share_examples_for :executor_service do
5
5
 
6
6
  after(:each) do
7
7
  subject.kill
@@ -10,74 +10,35 @@ share_examples_for :thread_pool do
10
10
 
11
11
  it_should_behave_like :global_thread_pool
12
12
 
13
- context '#scheduled_task_count' do
14
-
15
- it 'returns zero on creation' do
16
- subject.scheduled_task_count.should eq 0
17
- end
18
-
19
- it 'returns the approximate number of tasks that have been post thus far' do
20
- 10.times{ subject.post{ nil } }
21
- sleep(0.1)
22
- subject.scheduled_task_count.should > 0
23
- end
13
+ context '#post' do
24
14
 
25
- it 'returns the approximate number of tasks that were post' do
26
- 10.times{ subject.post{ nil } }
27
- sleep(0.1)
15
+ it 'rejects the block while shutting down' do
16
+ latch = Concurrent::CountDownLatch.new(1)
17
+ subject.post{ sleep(1) }
28
18
  subject.shutdown
29
- subject.wait_for_termination(1)
30
- subject.scheduled_task_count.should > 0
31
- end
32
- end
33
-
34
- context '#completed_task_count' do
35
-
36
- it 'returns zero on creation' do
37
- subject.completed_task_count.should eq 0
38
- end
39
-
40
- it 'returns the approximate number of tasks that have been completed thus far' do
41
- 5.times{ subject.post{ raise StandardError } }
42
- 5.times{ subject.post{ nil } }
43
- sleep(0.1)
44
- subject.completed_task_count.should > 0
19
+ subject.post{ latch.count_down }
20
+ latch.wait(0.1).should be_false
45
21
  end
46
22
 
47
- it 'returns the approximate number of tasks that were completed' do
48
- 5.times{ subject.post{ raise StandardError } }
49
- 5.times{ subject.post{ nil } }
50
- sleep(0.1)
23
+ it 'returns false while shutting down' do
24
+ subject.post{ sleep(1) }
51
25
  subject.shutdown
52
- subject.wait_for_termination(1)
53
- subject.completed_task_count.should > 0
54
- end
55
- end
56
-
57
- context '#length' do
58
-
59
- it 'returns zero on creation' do
60
- subject.length.should eq 0
26
+ subject.post{ nil }.should be_false
61
27
  end
62
28
 
63
- it 'returns a non-zero while shutting down' do
64
- 5.times{ subject.post{ sleep(0.1) } }
29
+ it 'rejects the block once shutdown' do
65
30
  subject.shutdown
66
- subject.length.should > 0
31
+ latch = Concurrent::CountDownLatch.new(1)
32
+ subject.post{ sleep(1) }
33
+ subject.post{ latch.count_down }
34
+ latch.wait(0.1).should be_false
67
35
  end
68
36
 
69
- it 'returns zero once shut down' do
70
- 5.times{ subject.post{ sleep(0.1) } }
71
- sleep(0.1)
37
+ it 'returns false once shutdown' do
38
+ subject.post{ nil }
72
39
  subject.shutdown
73
- subject.wait_for_termination(1)
74
- subject.length.should eq 0
75
- end
76
-
77
- it 'aliased as #current_length' do
78
- 5.times{ subject.post{ sleep(0.1) } }
79
40
  sleep(0.1)
80
- subject.current_length.should eq subject.length
41
+ subject.post{ nil }.should be_false
81
42
  end
82
43
  end
83
44
 
@@ -137,15 +98,6 @@ share_examples_for :thread_pool do
137
98
  sleep(1)
138
99
  @expected.should be_true
139
100
  end
140
-
141
- it 'allows threads to exit normally' do
142
- 10.times{ subject << proc{ nil } }
143
- subject.length.should > 0
144
- sleep(0.1)
145
- subject.shutdown
146
- sleep(1)
147
- subject.length.should == 0
148
- end
149
101
  end
150
102
 
151
103
  context '#kill' do
@@ -167,19 +119,6 @@ share_examples_for :thread_pool do
167
119
  sleep(0.1)
168
120
  subject.post{ nil }.should be_false
169
121
  end
170
-
171
- it 'kills all threads' do
172
- unless jruby? # this check is incorrect, want to check for class
173
- pending('brittle test--counting threads is not reliable')
174
- before_thread_count = Thread.list.size
175
- 100.times { subject << proc{ sleep(1) } }
176
- sleep(0.1)
177
- Thread.list.size.should > before_thread_count
178
- subject.kill
179
- sleep(0.1)
180
- Thread.list.size.should == before_thread_count
181
- end
182
- end
183
122
  end
184
123
 
185
124
  context '#wait_for_termination' do
@@ -204,71 +143,97 @@ share_examples_for :thread_pool do
204
143
  end
205
144
 
206
145
  it 'returns false when shutdown fails to complete before timeout' do
207
- (subject.length + 10).times{ subject.post{ sleep(1) } }
146
+ 100.times{ subject.post{ sleep(1) } }
208
147
  sleep(0.1)
209
148
  subject.shutdown
210
149
  subject.wait_for_termination(0).should be_false
211
150
  end
212
151
  end
152
+ end
213
153
 
214
- context '#post' do
154
+ share_examples_for :thread_pool do
155
+
156
+ after(:each) do
157
+ subject.kill
158
+ sleep(0.1)
159
+ end
160
+
161
+ it_should_behave_like :executor_service
162
+
163
+ context '#length' do
215
164
 
216
- it 'raises an exception if no block is given' do
217
- lambda {
218
- subject.post
219
- }.should raise_error(ArgumentError)
165
+ it 'returns zero on creation' do
166
+ subject.length.should eq 0
220
167
  end
221
168
 
222
- it 'returns true when the block is added to the queue' do
223
- subject.post{ nil }.should be_true
169
+ it 'returns zero once shut down' do
170
+ 5.times{ subject.post{ sleep(0.1) } }
171
+ sleep(0.1)
172
+ subject.shutdown
173
+ subject.wait_for_termination(1)
174
+ subject.length.should eq 0
224
175
  end
225
176
 
226
- it 'calls the block with the given arguments' do
227
- @expected = nil
228
- subject.post(1, 2, 3) do |a, b, c|
229
- @expected = a + b + c
230
- end
177
+ it 'aliased as #current_length' do
178
+ 5.times{ subject.post{ sleep(0.1) } }
231
179
  sleep(0.1)
232
- @expected.should eq 6
180
+ subject.current_length.should eq subject.length
233
181
  end
182
+ end
234
183
 
235
- it 'rejects the block while shutting down' do
236
- subject.post{ sleep(1) }
237
- subject.shutdown
238
- @expected = nil
239
- subject.post(1, 2, 3) do |a, b, c|
240
- @expected = a + b + c
241
- end
242
- @expected.should be_nil
184
+ context '#scheduled_task_count' do
185
+
186
+ it 'returns zero on creation' do
187
+ subject.scheduled_task_count.should eq 0
243
188
  end
244
189
 
245
- it 'returns false while shutting down' do
246
- subject.post{ sleep(1) }
247
- subject.shutdown
248
- subject.post{ nil }.should be_false
190
+ it 'returns the approximate number of tasks that have been post thus far' do
191
+ 10.times{ subject.post{ nil } }
192
+ sleep(0.1)
193
+ subject.scheduled_task_count.should > 0
249
194
  end
250
195
 
251
- it 'rejects the block once shutdown' do
196
+ it 'returns the approximate number of tasks that were post' do
197
+ 10.times{ subject.post{ nil } }
198
+ sleep(0.1)
252
199
  subject.shutdown
253
- @expected = nil
254
- subject.post(1, 2, 3) do |a, b, c|
255
- @expected = a + b + c
256
- end
257
- @expected.should be_nil
200
+ subject.wait_for_termination(1)
201
+ subject.scheduled_task_count.should > 0
258
202
  end
203
+ end
259
204
 
260
- it 'returns false once shutdown' do
261
- subject.post{ nil }
262
- subject.shutdown
205
+ context '#completed_task_count' do
206
+
207
+ it 'returns zero on creation' do
208
+ subject.completed_task_count.should eq 0
209
+ end
210
+
211
+ it 'returns the approximate number of tasks that have been completed thus far' do
212
+ 5.times{ subject.post{ raise StandardError } }
213
+ 5.times{ subject.post{ nil } }
263
214
  sleep(0.1)
264
- subject.post{ nil }.should be_false
215
+ subject.completed_task_count.should > 0
265
216
  end
266
217
 
267
- it 'aliases #<<' do
268
- @expected = false
269
- subject << proc { @expected = true }
218
+ it 'returns the approximate number of tasks that were completed' do
219
+ 5.times{ subject.post{ raise StandardError } }
220
+ 5.times{ subject.post{ nil } }
270
221
  sleep(0.1)
271
- @expected.should be_true
222
+ subject.shutdown
223
+ subject.wait_for_termination(1)
224
+ subject.completed_task_count.should > 0
225
+ end
226
+ end
227
+
228
+ context '#shutdown' do
229
+
230
+ it 'allows threads to exit normally' do
231
+ 10.times{ subject << proc{ nil } }
232
+ subject.length.should > 0
233
+ sleep(0.1)
234
+ subject.shutdown
235
+ sleep(1)
236
+ subject.length.should == 0
272
237
  end
273
238
  end
274
239
  end
@@ -0,0 +1,183 @@
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe TimerSet do
6
+
7
+ subject{ TimerSet.new(executor: ImmediateExecutor.new) }
8
+
9
+ after(:each){ subject.kill }
10
+
11
+ it 'uses the executor given at construction' do
12
+ executor = double(:executor)
13
+ executor.should_receive(:post).with(no_args)
14
+ subject = TimerSet.new(executor: executor)
15
+ subject.post(0){ nil }
16
+ sleep(0.1)
17
+ end
18
+
19
+ it 'uses the global task pool be default' do
20
+ Concurrent.configuration.global_task_pool.should_receive(:post).with(no_args)
21
+ subject = TimerSet.new
22
+ subject.post(0){ nil }
23
+ sleep(0.1)
24
+ end
25
+
26
+ it 'executes a given task when given a Time' do
27
+ latch = CountDownLatch.new(1)
28
+ subject.post(Time.now + 0.1){ latch.count_down }
29
+ latch.wait(0.2).should be_true
30
+ end
31
+
32
+ it 'executes a given task when given an interval in seconds' do
33
+ latch = CountDownLatch.new(1)
34
+ subject.post(0.1){ latch.count_down }
35
+ latch.wait(0.2).should be_true
36
+ end
37
+
38
+ it 'passes all arguments to the task on execution' do
39
+ expected = nil
40
+ latch = CountDownLatch.new(1)
41
+ subject.post(0.1, 1, 2, 3) do |*args|
42
+ expected = args
43
+ latch.count_down
44
+ end
45
+ latch.wait(0.2).should be_true
46
+ expected.should eq [1, 2, 3]
47
+ end
48
+
49
+ it 'immediately posts a task when the delay is zero' do
50
+ Thread.should_not_receive(:new).with(any_args)
51
+ subject.post(0){ true }
52
+ end
53
+
54
+ it 'does not execute tasks early' do
55
+ expected = AtomicFixnum.new(0)
56
+ subject.post(0.2){ expected.increment }
57
+ sleep(0.15)
58
+ expected.value.should eq 0
59
+ sleep(0.10)
60
+ expected.value.should eq 1
61
+ end
62
+
63
+ it 'raises an exception when given a task with a past Time value' do
64
+ expect {
65
+ subject.post(Time.now - 10){ nil }
66
+ }.to raise_error(ArgumentError)
67
+ end
68
+
69
+ it 'raises an exception when given a task with a delay less than zero' do
70
+ expect {
71
+ subject.post(-10){ nil }
72
+ }.to raise_error(ArgumentError)
73
+ end
74
+
75
+ it 'raises an exception when no block given' do
76
+ expect {
77
+ subject.post(10)
78
+ }.to raise_error(ArgumentError)
79
+ end
80
+
81
+ it 'executes all tasks scheduled for the same time' do
82
+ latch = CountDownLatch.new(5)
83
+ 5.times{ subject.post(0.1){ latch.count_down } }
84
+ latch.wait(0.2).should be_true
85
+ end
86
+
87
+ it 'executes tasks with different times in schedule order' do
88
+ expected = []
89
+ 3.times{|i| subject.post(i/10){ expected << i } }
90
+ sleep(0.3)
91
+ expected.should eq [0, 1, 2]
92
+ end
93
+
94
+ it 'cancels all pending tasks on #shutdown' do
95
+ expected = AtomicFixnum.new(0)
96
+ 10.times{ subject.post(0.2){ expected.increment } }
97
+ sleep(0.1)
98
+ subject.shutdown
99
+ sleep(0.2)
100
+ expected.value.should eq 0
101
+ end
102
+
103
+ it 'cancels all pending tasks on #kill' do
104
+ expected = AtomicFixnum.new(0)
105
+ 10.times{ subject.post(0.2){ expected.increment } }
106
+ sleep(0.1)
107
+ subject.kill
108
+ sleep(0.2)
109
+ expected.value.should eq 0
110
+ end
111
+
112
+ it 'stops the monitor thread on #shutdown' do
113
+ timer_executor = subject.instance_variable_get(:@timer_executor)
114
+ subject.shutdown
115
+ sleep(0.1)
116
+ timer_executor.should_not be_running
117
+ end
118
+
119
+ it 'kills the monitor thread on #kill' do
120
+ timer_executor = subject.instance_variable_get(:@timer_executor)
121
+ subject.kill
122
+ sleep(0.1)
123
+ timer_executor.should_not be_running
124
+ end
125
+
126
+ it 'rejects tasks once shutdown' do
127
+ expected = AtomicFixnum.new(0)
128
+ subject.shutdown
129
+ sleep(0.1)
130
+ subject.post(0){ expected.increment }.should be_false
131
+ sleep(0.1)
132
+ expected.value.should eq 0
133
+ end
134
+
135
+ it 'rejects tasks once killed' do
136
+ expected = AtomicFixnum.new(0)
137
+ subject.kill
138
+ sleep(0.1)
139
+ subject.post(0){ expected.increment }.should be_false
140
+ sleep(0.1)
141
+ expected.value.should eq 0
142
+ end
143
+
144
+ it 'is running? when first created' do
145
+ subject.should be_running
146
+ subject.should_not be_shutdown
147
+ end
148
+
149
+ it 'is running? after tasks have been post' do
150
+ subject.post(0.1){ nil }
151
+ subject.should be_running
152
+ subject.should_not be_shutdown
153
+ end
154
+
155
+ it 'is shutdown? after shutdown completes' do
156
+ subject.shutdown
157
+ sleep(0.1)
158
+ subject.should_not be_running
159
+ subject.should be_shutdown
160
+ end
161
+
162
+ it 'is shutdown? after being killed' do
163
+ subject.kill
164
+ sleep(0.1)
165
+ subject.should_not be_running
166
+ subject.should be_shutdown
167
+ end
168
+
169
+ specify '#wait_for_termination returns true if shutdown completes before timeout' do
170
+ subject.post(0.1){ nil }
171
+ sleep(0.1)
172
+ subject.shutdown
173
+ subject.wait_for_termination(0.1).should be_true
174
+ end
175
+
176
+ specify '#wait_for_termination returns false on timeout' do
177
+ subject.post(0.1){ nil }
178
+ sleep(0.1)
179
+ # do not call shutdown -- force timeout
180
+ subject.wait_for_termination(0.1).should be_false
181
+ end
182
+ end
183
+ end