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.
- checksums.yaml +4 -4
- data/README.md +16 -0
- data/lib/concurrent.rb +9 -29
- data/lib/concurrent/{actor.rb → actor/actor.rb} +3 -3
- data/lib/concurrent/actor/actor_context.rb +77 -0
- data/lib/concurrent/actor/actor_ref.rb +67 -0
- data/lib/concurrent/{postable.rb → actor/postable.rb} +1 -1
- data/lib/concurrent/actor/simple_actor_ref.rb +94 -0
- data/lib/concurrent/actors.rb +5 -0
- data/lib/concurrent/agent.rb +81 -47
- data/lib/concurrent/async.rb +35 -35
- data/lib/concurrent/atomic/atomic_boolean.rb +157 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +170 -0
- data/lib/concurrent/{condition.rb → atomic/condition.rb} +0 -0
- data/lib/concurrent/{copy_on_notify_observer_set.rb → atomic/copy_on_notify_observer_set.rb} +48 -13
- data/lib/concurrent/{copy_on_write_observer_set.rb → atomic/copy_on_write_observer_set.rb} +41 -20
- data/lib/concurrent/atomic/count_down_latch.rb +116 -0
- data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
- data/lib/concurrent/atomic/event.rb +103 -0
- data/lib/concurrent/{thread_local_var.rb → atomic/thread_local_var.rb} +0 -0
- data/lib/concurrent/atomics.rb +9 -0
- data/lib/concurrent/channel/buffered_channel.rb +6 -4
- data/lib/concurrent/channel/channel.rb +30 -2
- data/lib/concurrent/channel/unbuffered_channel.rb +2 -2
- data/lib/concurrent/channel/waitable_list.rb +3 -1
- data/lib/concurrent/channels.rb +5 -0
- data/lib/concurrent/{channel → collection}/blocking_ring_buffer.rb +16 -5
- data/lib/concurrent/collection/priority_queue.rb +305 -0
- data/lib/concurrent/{channel → collection}/ring_buffer.rb +6 -1
- data/lib/concurrent/collections.rb +3 -0
- data/lib/concurrent/configuration.rb +68 -19
- data/lib/concurrent/dataflow.rb +9 -9
- data/lib/concurrent/delay.rb +21 -13
- data/lib/concurrent/dereferenceable.rb +40 -33
- data/lib/concurrent/exchanger.rb +3 -0
- data/lib/concurrent/{cached_thread_pool.rb → executor/cached_thread_pool.rb} +8 -9
- data/lib/concurrent/executor/executor.rb +222 -0
- data/lib/concurrent/{fixed_thread_pool.rb → executor/fixed_thread_pool.rb} +6 -7
- data/lib/concurrent/{immediate_executor.rb → executor/immediate_executor.rb} +5 -5
- data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
- data/lib/concurrent/{java_fixed_thread_pool.rb → executor/java_fixed_thread_pool.rb} +7 -11
- data/lib/concurrent/executor/java_single_thread_executor.rb +21 -0
- data/lib/concurrent/{java_thread_pool_executor.rb → executor/java_thread_pool_executor.rb} +66 -77
- data/lib/concurrent/executor/one_by_one.rb +65 -0
- data/lib/concurrent/{per_thread_executor.rb → executor/per_thread_executor.rb} +4 -4
- data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
- data/lib/concurrent/{ruby_fixed_thread_pool.rb → executor/ruby_fixed_thread_pool.rb} +5 -4
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +72 -0
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +282 -0
- data/lib/concurrent/{ruby_thread_pool_worker.rb → executor/ruby_thread_pool_worker.rb} +6 -6
- data/lib/concurrent/{safe_task_executor.rb → executor/safe_task_executor.rb} +20 -13
- data/lib/concurrent/executor/single_thread_executor.rb +35 -0
- data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
- data/lib/concurrent/executor/timer_set.rb +138 -0
- data/lib/concurrent/executors.rb +9 -0
- data/lib/concurrent/future.rb +39 -40
- data/lib/concurrent/ivar.rb +22 -15
- data/lib/concurrent/mvar.rb +2 -1
- data/lib/concurrent/obligation.rb +9 -3
- data/lib/concurrent/observable.rb +33 -0
- data/lib/concurrent/options_parser.rb +46 -0
- data/lib/concurrent/promise.rb +23 -24
- data/lib/concurrent/scheduled_task.rb +21 -45
- data/lib/concurrent/timer_task.rb +204 -126
- data/lib/concurrent/tvar.rb +1 -1
- data/lib/concurrent/utilities.rb +3 -36
- data/lib/concurrent/{processor_count.rb → utility/processor_count.rb} +1 -1
- data/lib/concurrent/utility/timeout.rb +36 -0
- data/lib/concurrent/utility/timer.rb +21 -0
- data/lib/concurrent/version.rb +1 -1
- data/lib/concurrent_ruby_ext.bundle +0 -0
- data/spec/concurrent/{actor_context_spec.rb → actor/actor_context_spec.rb} +0 -8
- data/spec/concurrent/{actor_ref_shared.rb → actor/actor_ref_shared.rb} +9 -59
- data/spec/concurrent/{actor_spec.rb → actor/actor_spec.rb} +43 -41
- data/spec/concurrent/{postable_shared.rb → actor/postable_shared.rb} +0 -0
- data/spec/concurrent/actor/simple_actor_ref_spec.rb +135 -0
- data/spec/concurrent/agent_spec.rb +160 -71
- data/spec/concurrent/atomic/atomic_boolean_spec.rb +172 -0
- data/spec/concurrent/atomic/atomic_fixnum_spec.rb +186 -0
- data/spec/concurrent/{condition_spec.rb → atomic/condition_spec.rb} +2 -2
- data/spec/concurrent/{copy_on_notify_observer_set_spec.rb → atomic/copy_on_notify_observer_set_spec.rb} +0 -0
- data/spec/concurrent/{copy_on_write_observer_set_spec.rb → atomic/copy_on_write_observer_set_spec.rb} +0 -0
- data/spec/concurrent/atomic/count_down_latch_spec.rb +151 -0
- data/spec/concurrent/atomic/cyclic_barrier_spec.rb +248 -0
- data/spec/concurrent/{event_spec.rb → atomic/event_spec.rb} +18 -3
- data/spec/concurrent/{observer_set_shared.rb → atomic/observer_set_shared.rb} +15 -6
- data/spec/concurrent/{thread_local_var_spec.rb → atomic/thread_local_var_spec.rb} +0 -0
- data/spec/concurrent/channel/buffered_channel_spec.rb +1 -1
- data/spec/concurrent/channel/channel_spec.rb +6 -4
- data/spec/concurrent/channel/probe_spec.rb +37 -9
- data/spec/concurrent/channel/unbuffered_channel_spec.rb +2 -2
- data/spec/concurrent/{channel → collection}/blocking_ring_buffer_spec.rb +0 -0
- data/spec/concurrent/collection/priority_queue_spec.rb +317 -0
- data/spec/concurrent/{channel → collection}/ring_buffer_spec.rb +0 -0
- data/spec/concurrent/configuration_spec.rb +4 -70
- data/spec/concurrent/dereferenceable_shared.rb +5 -4
- data/spec/concurrent/exchanger_spec.rb +10 -5
- data/spec/concurrent/{cached_thread_pool_shared.rb → executor/cached_thread_pool_shared.rb} +15 -37
- data/spec/concurrent/{fixed_thread_pool_shared.rb → executor/fixed_thread_pool_shared.rb} +0 -0
- data/spec/concurrent/{global_thread_pool_shared.rb → executor/global_thread_pool_shared.rb} +10 -8
- data/spec/concurrent/{immediate_executor_spec.rb → executor/immediate_executor_spec.rb} +0 -0
- data/spec/concurrent/{java_cached_thread_pool_spec.rb → executor/java_cached_thread_pool_spec.rb} +1 -21
- data/spec/concurrent/{java_fixed_thread_pool_spec.rb → executor/java_fixed_thread_pool_spec.rb} +0 -0
- data/spec/concurrent/executor/java_single_thread_executor_spec.rb +21 -0
- data/spec/concurrent/{java_thread_pool_executor_spec.rb → executor/java_thread_pool_executor_spec.rb} +0 -0
- data/spec/concurrent/{per_thread_executor_spec.rb → executor/per_thread_executor_spec.rb} +0 -4
- data/spec/concurrent/{ruby_cached_thread_pool_spec.rb → executor/ruby_cached_thread_pool_spec.rb} +1 -1
- data/spec/concurrent/{ruby_fixed_thread_pool_spec.rb → executor/ruby_fixed_thread_pool_spec.rb} +0 -0
- data/spec/concurrent/executor/ruby_single_thread_executor_spec.rb +18 -0
- data/spec/concurrent/{ruby_thread_pool_executor_spec.rb → executor/ruby_thread_pool_executor_spec.rb} +12 -24
- data/spec/concurrent/executor/safe_task_executor_spec.rb +103 -0
- data/spec/concurrent/{thread_pool_class_cast_spec.rb → executor/thread_pool_class_cast_spec.rb} +12 -0
- data/spec/concurrent/{thread_pool_executor_shared.rb → executor/thread_pool_executor_shared.rb} +0 -0
- data/spec/concurrent/{thread_pool_shared.rb → executor/thread_pool_shared.rb} +84 -119
- data/spec/concurrent/executor/timer_set_spec.rb +183 -0
- data/spec/concurrent/future_spec.rb +12 -0
- data/spec/concurrent/ivar_spec.rb +11 -1
- data/spec/concurrent/observable_shared.rb +173 -0
- data/spec/concurrent/observable_spec.rb +51 -0
- data/spec/concurrent/options_parser_spec.rb +71 -0
- data/spec/concurrent/runnable_shared.rb +6 -0
- data/spec/concurrent/scheduled_task_spec.rb +60 -40
- data/spec/concurrent/timer_task_spec.rb +130 -144
- data/spec/concurrent/{processor_count_spec.rb → utility/processor_count_spec.rb} +0 -0
- data/spec/concurrent/{utilities_spec.rb → utility/timeout_spec.rb} +0 -0
- data/spec/concurrent/utility/timer_spec.rb +52 -0
- metadata +147 -108
- data/lib/concurrent/actor_context.rb +0 -31
- data/lib/concurrent/actor_ref.rb +0 -39
- data/lib/concurrent/atomic.rb +0 -121
- data/lib/concurrent/channel/probe.rb +0 -19
- data/lib/concurrent/count_down_latch.rb +0 -60
- data/lib/concurrent/event.rb +0 -80
- data/lib/concurrent/java_cached_thread_pool.rb +0 -45
- data/lib/concurrent/ruby_cached_thread_pool.rb +0 -37
- data/lib/concurrent/ruby_thread_pool_executor.rb +0 -268
- data/lib/concurrent/simple_actor_ref.rb +0 -124
- data/lib/concurrent/thread_pool_executor.rb +0 -30
- data/spec/concurrent/atomic_spec.rb +0 -201
- data/spec/concurrent/count_down_latch_spec.rb +0 -125
- data/spec/concurrent/safe_task_executor_spec.rb +0 -58
- 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
|
data/spec/concurrent/{thread_pool_class_cast_spec.rb → executor/thread_pool_class_cast_spec.rb}
RENAMED
@@ -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
|
data/spec/concurrent/{thread_pool_executor_shared.rb → executor/thread_pool_executor_shared.rb}
RENAMED
File without changes
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require_relative 'global_thread_pool_shared'
|
3
3
|
|
4
|
-
share_examples_for :
|
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 '#
|
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 '
|
26
|
-
|
27
|
-
sleep(
|
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.
|
30
|
-
|
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
|
48
|
-
|
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.
|
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 '
|
64
|
-
5.times{ subject.post{ sleep(0.1) } }
|
29
|
+
it 'rejects the block once shutdown' do
|
65
30
|
subject.shutdown
|
66
|
-
|
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
|
70
|
-
|
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.
|
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
|
-
|
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
|
-
|
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 '
|
217
|
-
|
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
|
223
|
-
subject.post{
|
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 '
|
227
|
-
|
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
|
-
|
180
|
+
subject.current_length.should eq subject.length
|
233
181
|
end
|
182
|
+
end
|
234
183
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
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
|
246
|
-
subject.post{
|
247
|
-
|
248
|
-
subject.
|
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 '
|
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
|
-
|
254
|
-
subject.
|
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
|
-
|
261
|
-
|
262
|
-
|
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.
|
215
|
+
subject.completed_task_count.should > 0
|
265
216
|
end
|
266
217
|
|
267
|
-
it '
|
268
|
-
|
269
|
-
subject
|
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
|
-
|
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
|