Skip to content

Commit a20d5ee

Browse files
committed
Refactored tests for Condition and Exchanger.
1 parent 0b87569 commit a20d5ee

File tree

3 files changed

+161
-87
lines changed

3 files changed

+161
-87
lines changed

spec/concurrent/atomic/condition_spec.rb

Lines changed: 141 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,6 @@ module Concurrent
22

33
describe Condition do
44

5-
let(:mutex) { Mutex.new }
6-
subject{ Condition.new }
7-
8-
before(:each) do
9-
# rspec is not thread safe, without mutex initialization
10-
# we can experience race conditions
11-
mutex
12-
end
13-
145
context 'with no waiting threads' do
156
describe '#signal' do
167
it 'should return immediately' do
@@ -32,25 +23,47 @@ module Concurrent
3223
describe '#wait without timeout' do
3324

3425
it 'should block the thread' do
35-
latch = Concurrent::CountDownLatch.new
26+
latch_1 = Concurrent::CountDownLatch.new
27+
latch_2 = Concurrent::CountDownLatch.new
28+
mutex = Mutex.new
29+
3630
t = Thread.new do
3731
mutex.synchronize do
38-
latch.count_down
32+
latch_1.count_down
3933
subject.wait(mutex)
34+
latch_2.count_down
4035
end
4136
end
4237

43-
latch.wait(1)
38+
latch_1.wait(1)
39+
latch_2.wait(0.1)
4440
expect(t.status).to eq 'sleep'
41+
expect(latch_2.count).to eq 1
4542
t.kill
4643
end
4744

4845
it 'should return a woken up result when is woken up by #signal' do
4946
result = nil
50-
t = Thread.new { mutex.synchronize { result = subject.wait(mutex) } }
51-
sleep(0.1)
52-
mutex.synchronize { subject.signal }
53-
sleep(0.1)
47+
mutex = Mutex.new
48+
latch_1 = Concurrent::CountDownLatch.new
49+
latch_2 = Concurrent::CountDownLatch.new
50+
51+
t = Thread.new do
52+
mutex.synchronize do
53+
latch_1.count_down
54+
result = subject.wait(mutex)
55+
latch_2.count_down
56+
end
57+
end
58+
59+
latch_1.wait(1)
60+
61+
mutex.synchronize do
62+
subject.signal
63+
end
64+
65+
latch_2.wait(1)
66+
5467
expect(result).to be_woken_up
5568
expect(result).not_to be_timed_out
5669
expect(result.remaining_time).to be_nil
@@ -59,72 +72,136 @@ module Concurrent
5972

6073
it 'should return a woken up result when is woken up by #broadcast' do
6174
result = nil
62-
t = Thread.new { mutex.synchronize { result = subject.wait(mutex) } }
63-
sleep(0.1)
64-
mutex.synchronize { subject.broadcast }
65-
sleep(0.1)
75+
mutex = Mutex.new
76+
latch_1 = Concurrent::CountDownLatch.new
77+
latch_2 = Concurrent::CountDownLatch.new
78+
79+
t = Thread.new do
80+
mutex.synchronize do
81+
latch_1.count_down
82+
result = subject.wait(mutex)
83+
latch_2.count_down
84+
end
85+
end
86+
87+
latch_1.wait(1)
88+
89+
mutex.synchronize do
90+
subject.broadcast
91+
end
92+
93+
latch_2.wait(1)
94+
6695
expect(result).to be_woken_up
6796
expect(result).not_to be_timed_out
6897
expect(result.remaining_time).to be_nil
6998
expect(t.status).to be_falsey
7099
end
71100
end
72-
73101
end
74102

75103
context 'timeout' do
76104

77105
describe '#wait' do
78106

79107
it 'should block the thread' do
80-
latch = Concurrent::CountDownLatch.new
108+
latch_1 = Concurrent::CountDownLatch.new
109+
latch_2 = Concurrent::CountDownLatch.new
110+
mutex = Mutex.new
111+
81112
t = Thread.new do
82113
mutex.synchronize do
83-
latch.count_down
114+
latch_1.count_down
84115
subject.wait(mutex, 1)
116+
latch_2.count_down
85117
end
86118
end
87119

88-
latch.wait(1)
120+
latch_1.wait(1)
121+
latch_2.wait(0.1)
89122
expect(t.status).to eq 'sleep'
123+
expect(latch_2.count).to eq 1
90124
t.kill
91125
end
92126

93127
it 'should return remaining time when is woken up by #signal' do
94128
result = nil
95-
t = Thread.new { mutex.synchronize { result = subject.wait(mutex, 1) } }
96-
sleep(0.1)
97-
mutex.synchronize { subject.signal }
98-
sleep(0.1)
129+
mutex = Mutex.new
130+
latch_1 = Concurrent::CountDownLatch.new
131+
latch_2 = Concurrent::CountDownLatch.new
132+
133+
t = Thread.new do
134+
mutex.synchronize do
135+
latch_1.count_down
136+
result = subject.wait(mutex, 1)
137+
latch_2.count_down
138+
end
139+
end
140+
141+
latch_1.wait(1)
142+
143+
mutex.synchronize do
144+
sleep(0.1)
145+
subject.signal
146+
end
147+
148+
latch_2.wait(1)
149+
99150
expect(result).to be_woken_up
100151
expect(result).not_to be_timed_out
101-
expect(result.remaining_time).to be_within(0.1).of(0.85)
152+
expect(result.remaining_time).to be < 1.0
102153
expect(t.status).to be_falsey
103154
end
104155

105156
it 'should return remaining time when is woken up by #broadcast' do
106157
result = nil
107-
t = Thread.new { mutex.synchronize { result = subject.wait(mutex, 1) } }
108-
sleep(0.1)
109-
mutex.synchronize { subject.broadcast }
110-
sleep(0.1)
158+
mutex = Mutex.new
159+
latch_1 = Concurrent::CountDownLatch.new
160+
latch_2 = Concurrent::CountDownLatch.new
161+
162+
t = Thread.new do
163+
mutex.synchronize do
164+
latch_1.count_down
165+
result = subject.wait(mutex, 1)
166+
latch_2.count_down
167+
end
168+
end
169+
170+
latch_1.wait(1)
171+
172+
mutex.synchronize do
173+
sleep(0.1)
174+
subject.broadcast
175+
end
176+
177+
latch_2.wait(1)
178+
111179
expect(result).to be_woken_up
112180
expect(result).not_to be_timed_out
113-
expect(result.remaining_time).to be_within(0.1).of(0.85)
181+
expect(result.remaining_time).to be < 1.0
114182
expect(t.status).to be_falsey
115183
end
116184

117185
it 'should return 0 or negative number if timed out' do
118186
result = nil
119-
t = Thread.new { mutex.synchronize { result = subject.wait(mutex, 0.1) } }
120-
sleep(0.2)
187+
mutex = Mutex.new
188+
latch = Concurrent::CountDownLatch.new
189+
190+
t = Thread.new do
191+
mutex.synchronize do
192+
result = subject.wait(mutex, 0.1)
193+
latch.count_down
194+
end
195+
end
196+
197+
latch.wait(1)
198+
121199
expect(result).not_to be_woken_up
122200
expect(result).to be_timed_out
123201
expect(result.remaining_time).to be_less_than_or_equal_to(0)
124202
expect(t.status).to be_falsey
125203
end
126204
end
127-
128205
end
129206
end
130207

@@ -135,34 +212,53 @@ module Concurrent
135212
describe '#wait' do
136213

137214
it 'should block threads' do
138-
t1 = Thread.new { mutex.synchronize { subject.wait(mutex) } }
139-
t2 = Thread.new { mutex.synchronize { subject.wait(mutex) } }
215+
mutex = Mutex.new
216+
latch = Concurrent::CountDownLatch.new(2)
217+
t1 = Thread.new { mutex.synchronize { latch.count_down; subject.wait(mutex) } }
218+
t2 = Thread.new { mutex.synchronize { latch.count_down; subject.wait(mutex) } }
219+
latch.wait(1)
140220
sleep(0.1)
141221
[t1, t2].each { |t| expect(t.status).to eq 'sleep' }
142222
[t1, t2].each { |t| t.kill }
143223
end
144-
145224
end
146225

147226
describe '#signal' do
148227
it 'wakes up only one thread' do
149-
latch = CountDownLatch.new(2)
228+
latch_1 = Concurrent::CountDownLatch.new(2)
229+
latch_2 = Concurrent::CountDownLatch.new(2)
230+
mutex = Mutex.new
150231

151-
t1 = Thread.new { mutex.synchronize { subject.wait(mutex); latch.count_down } }
152-
t2 = Thread.new { mutex.synchronize { subject.wait(mutex); latch.count_down } }
232+
t1 = Thread.new do
233+
mutex.synchronize do
234+
latch_1.count_down
235+
subject.wait(mutex)
236+
latch_2.count_down
237+
end
238+
end
153239

240+
t2 = Thread.new do
241+
mutex.synchronize do
242+
latch_1.count_down
243+
subject.wait(mutex)
244+
latch_2.count_down
245+
end
246+
end
247+
248+
latch_1.wait(1)
154249
sleep(0.1)
155250
mutex.synchronize { subject.signal }
156-
sleep(0.2)
251+
sleep(0.1)
157252

158-
expect(latch.count).to eq 1
253+
expect(latch_2.count).to eq 1
159254
[t1, t2].each { |t| t.kill }
160255
end
161256
end
162257

163258
describe '#broadcast' do
164259
it 'wakes up all threads' do
165260
latch = CountDownLatch.new(2)
261+
mutex = Mutex.new
166262

167263
t1 = Thread.new { mutex.synchronize { subject.wait(mutex); latch.count_down } }
168264
t2 = Thread.new { mutex.synchronize { subject.wait(mutex); latch.count_down } }
@@ -176,8 +272,6 @@ module Concurrent
176272
end
177273
end
178274
end
179-
180275
end
181-
182276
end
183277
end

spec/concurrent/exchanger_spec.rb

Lines changed: 19 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,72 +4,52 @@ module Concurrent
44

55
describe Exchanger do
66

7-
subject { Exchanger.new }
8-
let!(:exchanger) { subject } # let is not thread safe, let! creates the object before ensuring uniqueness
9-
107
describe 'exchange' do
118

129
context 'without timeout' do
1310

1411
it 'should block' do
15-
latch = Concurrent::CountDownLatch.new
12+
latch_1 = Concurrent::CountDownLatch.new
13+
latch_2 = Concurrent::CountDownLatch.new
1614

1715
t = Thread.new do
18-
latch.count_down
19-
exchanger.exchange(1)
16+
latch_1.count_down
17+
subject.exchange(1)
18+
latch_2.count_down
2019
end
2120

22-
latch.wait(1)
21+
latch_1.wait(1)
22+
latch_2.wait(0.1)
2323
expect(t.status).to eq 'sleep'
24+
expect(latch_2.count).to eq 1
25+
t.kill
2426
end
2527

2628
it 'should receive the other value' do
27-
latch = Concurrent::CountDownLatch.new(2)
2829
first_value = nil
2930
second_value = nil
3031

31-
Thread.new do
32-
first_value = exchanger.exchange(2)
33-
latch.count_down
34-
end
35-
Thread.new do
36-
second_value = exchanger.exchange(4)
37-
latch.count_down
38-
end
32+
thread_1 = Thread.new { first_value = subject.exchange(2) }
33+
thread_2 = Thread.new { second_value = subject.exchange(4) }
3934

40-
latch.wait(1)
35+
[thread_1, thread_2].each(&:join)
4136
expect(first_value).to eq 4
4237
expect(second_value).to eq 2
4338
end
4439

4540
it 'can be reused' do
46-
latch_1 = Concurrent::CountDownLatch.new(2)
47-
latch_2 = Concurrent::CountDownLatch.new(2)
48-
4941
first_value = nil
5042
second_value = nil
5143

52-
Thread.new do
53-
first_value = exchanger.exchange(1)
54-
latch_1.count_down
55-
end
56-
Thread.new do
57-
second_value = exchanger.exchange(0)
58-
latch_1.count_down
59-
end
44+
thread_1 = Thread.new { first_value = subject.exchange(1) }
45+
thread_2 = Thread.new { second_value = subject.exchange(0) }
6046

61-
latch_1.wait(1)
47+
[thread_1, thread_2].each(&:join)
6248

63-
Thread.new do
64-
first_value = exchanger.exchange(10)
65-
latch_2.count_down
66-
end
67-
Thread.new do
68-
second_value = exchanger.exchange(12)
69-
latch_2.count_down
70-
end
49+
thread_1 = Thread.new { first_value = subject.exchange(10) }
50+
thread_2 = Thread.new { second_value = subject.exchange(12) }
7151

72-
latch_2.wait(1)
52+
[thread_1, thread_2].each(&:join)
7353

7454
expect(first_value).to eq 12
7555
expect(second_value).to eq 10
@@ -80,7 +60,7 @@ module Concurrent
8060

8161
it 'should block until timeout' do
8262
duration = Hitimes::Interval.measure do
83-
exchanger.exchange(2, 0.1)
63+
subject.exchange(2, 0.1)
8464
end
8565
expect(duration).to be_within(0.05).of(0.1)
8666
end

0 commit comments

Comments
 (0)