-
Notifications
You must be signed in to change notification settings - Fork 28
/
count_down_latch.rb
151 lines (136 loc) · 3.45 KB
/
count_down_latch.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#!/usr/bin/env ruby
# coding: utf-8
require 'thread'
class CountDownLatch
attr_reader :count
def initialize(to)
@count = to.to_i
raise ArgumentError, "cannot count down from negative integer" unless @count >= 0
@lock = Mutex.new
@condition = ConditionVariable.new
end
def count_down
@lock.synchronize do
@count -= 1 if @count > 0
@condition.broadcast if @count == 0
end
end
def wait
@lock.synchronize do
@condition.wait(@lock) while @count > 0
end
end
end
if $0 == __FILE__
require 'test/unit'
class CountDownLatchTest < Test::Unit::TestCase
def test_requires_positive_count
assert_raise(ArgumentError) { CountDownLatch.new(-1) }
end
def test_basic_latch_usage
latch = CountDownLatch.new(1)
name = "foo"
Thread.new do
name = "bar"
latch.count_down
end
latch.wait
assert_equal(0, latch.count)
assert_equal("bar", name)
end
def test_basic_latch_usage_inverted
latch = CountDownLatch.new(1)
name = "foo"
Thread.new do
latch.wait
assert_equal(0, latch.count)
assert_equal("bar", name)
end
name = "bar"
latch.count_down
end
def test_count_down_from_zero_skips_wait
latch = CountDownLatch.new(0)
latch.wait
assert_equal(0, latch.count)
end
def test_count_down_twice_with_thread
latch = CountDownLatch.new(2)
name = "foo"
Thread.new do
latch.count_down
name = "bar"
latch.count_down
end
latch.wait
assert_equal(0, latch.count)
assert_equal("bar", name)
end
def test_count_down_twice_with_two_parallel_threads
latch = CountDownLatch.new(2)
name = "foo"
Thread.new { latch.count_down }
Thread.new do
name = "bar"
latch.count_down
end
latch.wait
assert_equal(0, latch.count)
assert_equal("bar", name)
end
def test_count_down_twice_with_two_chained_threads
latch = CountDownLatch.new(2)
name = "foo"
Thread.new do
latch.count_down
Thread.new do
name = "bar"
latch.count_down
end
end
latch.wait
assert_equal(0, latch.count)
assert_equal("bar", name)
end
def test_count_down_with_multiple_waiters
proceed_latch = CountDownLatch.new(2)
check_latch = CountDownLatch.new(2)
results = {}
Thread.new do
proceed_latch.wait
results[:first] = 1
check_latch.count_down
end
Thread.new do
proceed_latch.wait
results[:second] = 2
check_latch.count_down
end
assert_equal({}, results)
proceed_latch.count_down
proceed_latch.count_down
check_latch.wait
assert_equal(0, proceed_latch.count)
assert_equal(0, check_latch.count)
assert_equal({:first => 1, :second => 2}, results)
end
def test_interleaved_latches
change_1_latch = CountDownLatch.new(1)
check_latch = CountDownLatch.new(1)
change_2_latch = CountDownLatch.new(1)
name = "foo"
Thread.new do
name = "bar"
change_1_latch.count_down
check_latch.wait
name = "man"
change_2_latch.count_down
end
change_1_latch.wait
assert_equal("bar", name)
check_latch.count_down
change_2_latch.wait
assert_equal("man", name)
end
end
end