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,186 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
share_examples_for :atomic_fixnum do
|
4
|
+
|
5
|
+
context 'construction' do
|
6
|
+
|
7
|
+
it 'sets the initial value' do
|
8
|
+
described_class.new(10).value.should eq 10
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'defaults the initial value to zero' do
|
12
|
+
described_class.new.value.should eq 0
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'raises en exception if the initial value is not a Fixnum' do
|
16
|
+
lambda {
|
17
|
+
described_class.new(10.01)
|
18
|
+
}.should raise_error
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context '#value' do
|
23
|
+
|
24
|
+
it 'returns the current value' do
|
25
|
+
counter = described_class.new(10)
|
26
|
+
counter.value.should eq 10
|
27
|
+
counter.increment
|
28
|
+
counter.value.should eq 11
|
29
|
+
counter.decrement
|
30
|
+
counter.value.should eq 10
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context '#value=' do
|
35
|
+
|
36
|
+
it 'sets the #value to the given `Fixnum`' do
|
37
|
+
atomic = described_class.new(0)
|
38
|
+
atomic.value = 10
|
39
|
+
atomic.value.should eq 10
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'returns the new value' do
|
43
|
+
atomic = described_class.new(0)
|
44
|
+
(atomic.value = 10).should eq 10
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'raises and exception if the value is not a `Fixnum`' do
|
48
|
+
atomic = described_class.new(0)
|
49
|
+
expect {
|
50
|
+
atomic.value = 'foo'
|
51
|
+
}.to raise_error
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context '#increment' do
|
56
|
+
|
57
|
+
it 'increases the value by one' do
|
58
|
+
counter = described_class.new(10)
|
59
|
+
3.times{ counter.increment }
|
60
|
+
counter.value.should eq 13
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'returns the new value' do
|
64
|
+
counter = described_class.new(10)
|
65
|
+
counter.increment.should eq 11
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'is aliased as #up' do
|
69
|
+
described_class.new(10).up.should eq 11
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context '#decrement' do
|
74
|
+
|
75
|
+
it 'decreases the value by one' do
|
76
|
+
counter = described_class.new(10)
|
77
|
+
3.times{ counter.decrement }
|
78
|
+
counter.value.should eq 7
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'returns the new value' do
|
82
|
+
counter = described_class.new(10)
|
83
|
+
counter.decrement.should eq 9
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'is aliased as #down' do
|
87
|
+
described_class.new(10).down.should eq 9
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context '#compare_and_set' do
|
92
|
+
|
93
|
+
it 'returns false if the value is not found' do
|
94
|
+
described_class.new(14).compare_and_set(2, 14).should eq false
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'returns true if the value is found' do
|
98
|
+
described_class.new(14).compare_and_set(14, 2).should eq true
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'sets if the value is found' do
|
102
|
+
f = described_class.new(14)
|
103
|
+
f.compare_and_set(14, 2)
|
104
|
+
f.value.should eq 2
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'does not set if the value is not found' do
|
108
|
+
f = described_class.new(14)
|
109
|
+
f.compare_and_set(2, 12)
|
110
|
+
f.value.should eq 14
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
module Concurrent
|
116
|
+
|
117
|
+
describe MutexAtomicFixnum do
|
118
|
+
|
119
|
+
it_should_behave_like :atomic_fixnum
|
120
|
+
|
121
|
+
specify 'construction is synchronized' do
|
122
|
+
mutex = double('mutex')
|
123
|
+
Mutex.should_receive(:new).once.with(no_args).and_return(mutex)
|
124
|
+
described_class.new
|
125
|
+
end
|
126
|
+
|
127
|
+
specify 'value is synchronized' do
|
128
|
+
mutex = double('mutex')
|
129
|
+
Mutex.stub(:new).with(no_args).and_return(mutex)
|
130
|
+
mutex.should_receive(:lock)
|
131
|
+
mutex.should_receive(:unlock)
|
132
|
+
described_class.new.value
|
133
|
+
end
|
134
|
+
|
135
|
+
specify 'value= is synchronized' do
|
136
|
+
mutex = double('mutex')
|
137
|
+
Mutex.stub(:new).with(no_args).and_return(mutex)
|
138
|
+
mutex.should_receive(:lock)
|
139
|
+
mutex.should_receive(:unlock)
|
140
|
+
described_class.new.value = 10
|
141
|
+
end
|
142
|
+
|
143
|
+
specify 'increment is synchronized' do
|
144
|
+
mutex = double('mutex')
|
145
|
+
Mutex.stub(:new).with(no_args).and_return(mutex)
|
146
|
+
mutex.should_receive(:lock)
|
147
|
+
mutex.should_receive(:unlock)
|
148
|
+
described_class.new.increment
|
149
|
+
end
|
150
|
+
|
151
|
+
specify 'decrement is synchronized' do
|
152
|
+
mutex = double('mutex')
|
153
|
+
Mutex.stub(:new).with(no_args).and_return(mutex)
|
154
|
+
mutex.should_receive(:lock)
|
155
|
+
mutex.should_receive(:unlock)
|
156
|
+
described_class.new.decrement
|
157
|
+
end
|
158
|
+
|
159
|
+
specify 'compare_and_set is synchronized' do
|
160
|
+
mutex = double('mutex')
|
161
|
+
Mutex.stub(:new).with(no_args).and_return(mutex)
|
162
|
+
mutex.should_receive(:lock)
|
163
|
+
mutex.should_receive(:unlock)
|
164
|
+
described_class.new(14).compare_and_set(14, 2)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
if jruby?
|
169
|
+
|
170
|
+
describe JavaAtomicFixnum do
|
171
|
+
it_should_behave_like :atomic_fixnum
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe AtomicFixnum do
|
176
|
+
if jruby?
|
177
|
+
it 'inherits from JavaAtomicFixnum' do
|
178
|
+
AtomicFixnum.ancestors.should include(JavaAtomicFixnum)
|
179
|
+
end
|
180
|
+
else
|
181
|
+
it 'inherits from MutexAtomicFixnum' do
|
182
|
+
AtomicFixnum.ancestors.should include(MutexAtomicFixnum)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -86,7 +86,7 @@ module Concurrent
|
|
86
86
|
sleep(0.1)
|
87
87
|
result.should be_woken_up
|
88
88
|
result.should_not be_timed_out
|
89
|
-
result.remaining_time.should be_within(0.
|
89
|
+
result.remaining_time.should be_within(0.1).of(0.85)
|
90
90
|
t.status.should be_false
|
91
91
|
end
|
92
92
|
|
@@ -98,7 +98,7 @@ module Concurrent
|
|
98
98
|
sleep(0.1)
|
99
99
|
result.should be_woken_up
|
100
100
|
result.should_not be_timed_out
|
101
|
-
result.remaining_time.should be_within(0.
|
101
|
+
result.remaining_time.should be_within(0.1).of(0.85)
|
102
102
|
t.status.should be_false
|
103
103
|
end
|
104
104
|
|
File without changes
|
File without changes
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
share_examples_for :count_down_latch do
|
4
|
+
|
5
|
+
let(:latch) { described_class.new(3) }
|
6
|
+
let(:zero_count_latch) { described_class.new(0) }
|
7
|
+
|
8
|
+
context '#initialize' do
|
9
|
+
|
10
|
+
it 'raises an exception if the initial count is less than zero' do
|
11
|
+
expect {
|
12
|
+
described_class.new(-1)
|
13
|
+
}.to raise_error(ArgumentError)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'raises an exception if the initial count is not an integer' do
|
17
|
+
expect {
|
18
|
+
described_class.new('foo')
|
19
|
+
}.to raise_error(ArgumentError)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#count' do
|
24
|
+
|
25
|
+
it 'should be the value passed to the constructor' do
|
26
|
+
latch.count.should eq 3
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should be decreased after every count down' do
|
30
|
+
latch.count_down
|
31
|
+
latch.count.should eq 2
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should not go below zero' do
|
35
|
+
5.times { latch.count_down }
|
36
|
+
latch.count.should eq 0
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#wait' do
|
41
|
+
|
42
|
+
context 'count set to zero' do
|
43
|
+
it 'should return true immediately' do
|
44
|
+
result = zero_count_latch.wait
|
45
|
+
result.should be_true
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should return true immediately with timeout' do
|
49
|
+
result = zero_count_latch.wait(5)
|
50
|
+
result.should be_true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'non zero count' do
|
55
|
+
|
56
|
+
it 'should block thread until counter is set to zero' do
|
57
|
+
3.times do
|
58
|
+
Thread.new { sleep(0.1); latch.count_down }
|
59
|
+
end
|
60
|
+
|
61
|
+
result = latch.wait
|
62
|
+
result.should be_true
|
63
|
+
latch.count.should eq 0
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should block until counter is set to zero with timeout' do
|
67
|
+
3.times do
|
68
|
+
Thread.new { sleep(0.1); latch.count_down }
|
69
|
+
end
|
70
|
+
|
71
|
+
result = latch.wait(1)
|
72
|
+
result.should be_true
|
73
|
+
latch.count.should eq 0
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should block until timeout and return false when counter is not set to zero' do
|
78
|
+
result = latch.wait(0.1)
|
79
|
+
result.should be_false
|
80
|
+
latch.count.should eq 3
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
module Concurrent
|
87
|
+
|
88
|
+
describe MutexCountDownLatch do
|
89
|
+
|
90
|
+
it_should_behave_like :count_down_latch
|
91
|
+
|
92
|
+
context 'spurious wake ups' do
|
93
|
+
|
94
|
+
subject { described_class.new(3) }
|
95
|
+
|
96
|
+
before(:each) do
|
97
|
+
def subject.simulate_spurious_wake_up
|
98
|
+
@mutex.synchronize do
|
99
|
+
@condition.signal
|
100
|
+
@condition.broadcast
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should resist to spurious wake ups without timeout' do
|
106
|
+
@expected = false
|
107
|
+
Thread.new { subject.wait; @expected = true }
|
108
|
+
|
109
|
+
sleep(0.1)
|
110
|
+
subject.simulate_spurious_wake_up
|
111
|
+
|
112
|
+
sleep(0.1)
|
113
|
+
@expected.should be_false
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'should resist to spurious wake ups with timeout' do
|
117
|
+
@expected = false
|
118
|
+
Thread.new { subject.wait(0.5); @expected = true }
|
119
|
+
|
120
|
+
sleep(0.1)
|
121
|
+
subject.simulate_spurious_wake_up
|
122
|
+
|
123
|
+
sleep(0.1)
|
124
|
+
@expected.should be_false
|
125
|
+
|
126
|
+
sleep(0.4)
|
127
|
+
@expected.should be_true
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
if jruby?
|
133
|
+
|
134
|
+
describe JavaCountDownLatch do
|
135
|
+
|
136
|
+
it_should_behave_like :count_down_latch
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe CountDownLatch do
|
141
|
+
if jruby?
|
142
|
+
it 'inherits from JavaCountDownLatch' do
|
143
|
+
CountDownLatch.ancestors.should include(JavaCountDownLatch)
|
144
|
+
end
|
145
|
+
else
|
146
|
+
it 'inherits from MutexCountDownLatch' do
|
147
|
+
CountDownLatch.ancestors.should include(MutexCountDownLatch)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,248 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
describe CyclicBarrier do
|
6
|
+
|
7
|
+
let(:parties) { 3 }
|
8
|
+
let!(:barrier) { described_class.new(3) }
|
9
|
+
|
10
|
+
context '#initialize' do
|
11
|
+
|
12
|
+
it 'raises an exception if the initial count is less than 1' do
|
13
|
+
expect {
|
14
|
+
described_class.new(0)
|
15
|
+
}.to raise_error(ArgumentError)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'raises an exception if the initial count is not an integer' do
|
19
|
+
expect {
|
20
|
+
described_class.new('foo')
|
21
|
+
}.to raise_error(ArgumentError)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#parties' do
|
26
|
+
|
27
|
+
it 'should be the value passed to the constructor' do
|
28
|
+
barrier.parties.should eq 3
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#number_waiting' do
|
34
|
+
context 'without any waiting thread' do
|
35
|
+
it 'should be equal to zero' do
|
36
|
+
barrier.number_waiting.should eq 0
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'with waiting threads' do
|
41
|
+
it 'should be equal to the waiting threads count' do
|
42
|
+
Thread.new { barrier.wait }
|
43
|
+
Thread.new { barrier.wait }
|
44
|
+
|
45
|
+
sleep(0.1)
|
46
|
+
|
47
|
+
barrier.number_waiting.should eq 2
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#broken?' do
|
53
|
+
it 'should not be broken when created' do
|
54
|
+
barrier.broken?.should eq false
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should not be broken when reset is called without waiting thread' do
|
58
|
+
barrier.reset
|
59
|
+
barrier.broken?.should eq false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe 'reset' do
|
64
|
+
it 'should release all waiting threads' do
|
65
|
+
latch = CountDownLatch.new(1)
|
66
|
+
|
67
|
+
Thread.new { barrier.wait; latch.count_down }
|
68
|
+
sleep(0.1)
|
69
|
+
barrier.reset
|
70
|
+
latch.wait(0.1).should be_true
|
71
|
+
|
72
|
+
barrier.should_not be_broken
|
73
|
+
barrier.number_waiting.should eq 0
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '#wait' do
|
78
|
+
context 'without timeout' do
|
79
|
+
it 'should block the thread' do
|
80
|
+
t = Thread.new { barrier.wait }
|
81
|
+
sleep(0.1)
|
82
|
+
|
83
|
+
t.status.should eq 'sleep'
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should release all threads when their number matches the desired one' do
|
87
|
+
latch = CountDownLatch.new(parties)
|
88
|
+
|
89
|
+
parties.times { Thread.new { barrier.wait; latch.count_down } }
|
90
|
+
latch.wait(0.1).should be_true
|
91
|
+
barrier.number_waiting.should eq 0
|
92
|
+
barrier.should_not be_broken
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'returns true when released' do
|
96
|
+
latch = CountDownLatch.new(parties)
|
97
|
+
|
98
|
+
parties.times { Thread.new { latch.count_down if barrier.wait == true } }
|
99
|
+
latch.wait(0.1).should be_true
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'executes the block once' do
|
103
|
+
counter = AtomicFixnum.new
|
104
|
+
barrier = described_class.new(parties) { counter.increment }
|
105
|
+
|
106
|
+
latch = CountDownLatch.new(parties)
|
107
|
+
|
108
|
+
parties.times { Thread.new { latch.count_down if barrier.wait == true } }
|
109
|
+
latch.wait(0.1).should be_true
|
110
|
+
|
111
|
+
counter.value.should eq 1
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'can be reused' do
|
115
|
+
first_latch = CountDownLatch.new(parties)
|
116
|
+
parties.times { Thread.new { barrier.wait; first_latch.count_down } }
|
117
|
+
first_latch.wait(0.1).should be_true
|
118
|
+
|
119
|
+
latch = CountDownLatch.new(parties)
|
120
|
+
parties.times { Thread.new { barrier.wait; latch.count_down } }
|
121
|
+
latch.wait(0.1).should be_true
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'return false if barrier has been reset' do
|
125
|
+
latch = CountDownLatch.new(1)
|
126
|
+
|
127
|
+
Thread.new { latch.count_down if barrier.wait == false }
|
128
|
+
sleep(0.1)
|
129
|
+
barrier.reset
|
130
|
+
latch.wait(0.1).should be_true
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context 'with timeout' do
|
135
|
+
context 'timeout not expiring' do
|
136
|
+
it 'should block the thread' do
|
137
|
+
t = Thread.new { barrier.wait(1) }
|
138
|
+
sleep(0.1)
|
139
|
+
|
140
|
+
t.status.should eq 'sleep'
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'should release all threads when their number matches the desired one' do
|
144
|
+
latch = CountDownLatch.new(parties)
|
145
|
+
|
146
|
+
parties.times { Thread.new { barrier.wait(1); latch.count_down } }
|
147
|
+
latch.wait(0.2).should be_true
|
148
|
+
barrier.number_waiting.should eq 0
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'returns true when released' do
|
152
|
+
latch = CountDownLatch.new(parties)
|
153
|
+
|
154
|
+
parties.times { Thread.new { latch.count_down if barrier.wait(1) == true } }
|
155
|
+
latch.wait(0.1).should be_true
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context 'timeout expiring' do
|
160
|
+
|
161
|
+
it 'returns false' do
|
162
|
+
latch = CountDownLatch.new(1)
|
163
|
+
|
164
|
+
Thread.new { latch.count_down if barrier.wait(0.1) == false }
|
165
|
+
latch.wait(0.2).should be_true
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'breaks the barrier and release all other threads' do
|
169
|
+
latch = CountDownLatch.new(2)
|
170
|
+
|
171
|
+
Thread.new { barrier.wait(0.1); latch.count_down }
|
172
|
+
Thread.new { barrier.wait; latch.count_down }
|
173
|
+
|
174
|
+
latch.wait(0.2).should be_true
|
175
|
+
barrier.should be_broken
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'does not execute the block on timeout' do
|
179
|
+
counter = AtomicFixnum.new
|
180
|
+
barrier = described_class.new(parties) { counter.increment }
|
181
|
+
|
182
|
+
barrier.wait(0.1)
|
183
|
+
|
184
|
+
counter.value.should eq 0
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
context '#broken barrier' do
|
190
|
+
it 'should not accept new threads' do
|
191
|
+
Thread.new { barrier.wait(0.1) }
|
192
|
+
sleep(0.2)
|
193
|
+
|
194
|
+
barrier.should be_broken
|
195
|
+
|
196
|
+
barrier.wait.should be_false
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'can be reset' do
|
200
|
+
Thread.new { barrier.wait(0.1) }
|
201
|
+
sleep(0.2)
|
202
|
+
|
203
|
+
barrier.should be_broken
|
204
|
+
|
205
|
+
barrier.reset
|
206
|
+
|
207
|
+
barrier.should_not be_broken
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
context 'spurious wake ups' do
|
213
|
+
|
214
|
+
before(:each) do
|
215
|
+
def barrier.simulate_spurious_wake_up
|
216
|
+
@mutex.synchronize do
|
217
|
+
@condition.signal
|
218
|
+
@condition.broadcast
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'should resist to spurious wake ups without timeout' do
|
224
|
+
@expected = false
|
225
|
+
Thread.new { barrier.wait; @expected = true }
|
226
|
+
|
227
|
+
sleep(0.1)
|
228
|
+
barrier.simulate_spurious_wake_up
|
229
|
+
|
230
|
+
sleep(0.1)
|
231
|
+
@expected.should be_false
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'should resist to spurious wake ups with timeout' do
|
235
|
+
@expected = false
|
236
|
+
Thread.new { barrier.wait(0.5); @expected = true }
|
237
|
+
|
238
|
+
sleep(0.1)
|
239
|
+
barrier.simulate_spurious_wake_up
|
240
|
+
|
241
|
+
sleep(0.1)
|
242
|
+
@expected.should be_false
|
243
|
+
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|