Skip to content

Commit ff717f7

Browse files
committed
added pending test and begun implementation
1 parent bf21734 commit ff717f7

File tree

2 files changed

+108
-2
lines changed

2 files changed

+108
-2
lines changed

lib/concurrent/atomic/cyclic_barrier.rb

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,51 @@ class CyclicBarrier
99
#
1010
# @raise [ArgumentError] if `parties` is not an integer or is less than zero
1111
def initialize(parties)
12+
raise ArgumentError.new('count must be in integer greater than or equal zero') if !parties.is_a?(Fixnum) || parties < 1
13+
@parties = parties
14+
@mutex = Mutex.new
15+
@condition = Condition.new
16+
@number_waiting = 0
1217
end
1318

1419
# @return [Fixnum] the number of threads needed to pass the barrier
1520
def parties
21+
@parties
1622
end
1723

1824
# @return [Fixnum] the number of threads currently waiting on the barrier
1925
def number_waiting
26+
@number_waiting
2027
end
2128

2229
# Blocks on the barrier until the number of waiting threads is equal to `parties` or until `timeout` is reached or `reset` is called
2330
# @param [Fixnum] timeout the number of seconds to wait for the counter or `nil` to block indefinitely
2431
# @return [Boolean] `true` if the `count` reaches zero else false on `timeout` or on `reset`
2532
def wait(timeout = nil)
33+
@mutex.synchronize do
34+
@number_waiting += 1
35+
if @number_waiting == @parties
36+
@condition.broadcast
37+
@number_waiting = 0
38+
else
39+
@condition.wait(@mutex)
40+
end
41+
end
2642
end
2743

2844
# resets the barrier to its initial state
2945
# If there is at least one waiting thread, it will be woken up, the `wait` method will return false and the barrier will be broken
46+
# If the barrier is broken, this method restores it to the original state
3047
#
3148
# @return [nil]
3249
def reset
3350
end
3451

3552
# A barrier can be broken when:
36-
# - a thread called the `reset` method while at least one thread was waiting
53+
# - a thread called the `reset` method while at least one other thread was waiting
3754
# - at least one thread timed out on `wait` method
3855
#
39-
# A broken barrier cannot be restored and it should not be reused: it's safer to create a new one
56+
# A broken barrier can be restored using `reset` it's safer to create a new one
4057
# @return [Boolean] true if the barrier is broken otherwise false
4158
def broken?
4259
end

spec/concurrent/atomic/cyclic_barrier_spec.rb

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,95 @@ module Concurrent
44

55
describe CyclicBarrier do
66

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'
54+
it 'should not be broken when reset is called without waiting thread'
55+
it 'should be broken when at least one thread timed out'
56+
it 'should be restored when reset is called'
57+
end
58+
59+
describe 'reset' do
60+
it 'should release all waiting threads'
61+
it 'should not execute the block'
62+
end
63+
64+
describe '#wait' do
65+
context 'without timeout' do
66+
it 'should block the thread' do
67+
t = Thread.new { barrier.wait }
68+
sleep(0.1)
69+
70+
t.status.should eq 'sleep'
71+
end
72+
73+
it 'should release all threads when their number matches the desired one' do
74+
latch = CountDownLatch.new(parties)
75+
76+
parties.times { Thread.new { barrier.wait; latch.count_down } }
77+
latch.wait(0.2).should be_true
78+
barrier.number_waiting.should eq 0
79+
end
80+
81+
it 'executes the block'
82+
end
83+
84+
context 'with timeout' do
85+
it 'should block the thread'
86+
it 'should release all threads when their number matches the desired one'
87+
it 'can return early and break the barrier'
88+
it 'does not execute the block on timeout'
89+
end
90+
end
91+
92+
context 'spurious wakeups' do
93+
it 'should resist'
94+
end
95+
796
end
897

998

0 commit comments

Comments
 (0)