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

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