quack_concurrency 0.5.4 → 0.6.0
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 +1 -1
- data/lib/quack_concurrency.rb +6 -13
- data/lib/quack_concurrency/condition_variable.rb +91 -85
- data/lib/quack_concurrency/condition_variable/waitable.rb +108 -0
- data/lib/quack_concurrency/error.rb +1 -1
- data/lib/quack_concurrency/future.rb +31 -30
- data/lib/quack_concurrency/future/canceled.rb +1 -0
- data/lib/quack_concurrency/future/complete.rb +1 -0
- data/lib/quack_concurrency/mutex.rb +140 -38
- data/lib/quack_concurrency/queue.rb +32 -28
- data/lib/quack_concurrency/reentrant_mutex.rb +64 -76
- data/lib/quack_concurrency/safe_condition_variable.rb +23 -0
- data/lib/quack_concurrency/safe_condition_variable/waitable.rb +21 -0
- data/lib/quack_concurrency/safe_sleeper.rb +80 -0
- data/lib/quack_concurrency/sleeper.rb +100 -0
- data/lib/quack_concurrency/waiter.rb +32 -23
- data/spec/condition_variable_spec.rb +216 -0
- data/spec/future_spec.rb +145 -79
- data/spec/mutex_spec.rb +441 -0
- data/spec/queue_spec.rb +217 -77
- data/spec/reentrant_mutex_spec.rb +394 -99
- data/spec/safe_condition_variable_spec.rb +115 -0
- data/spec/safe_sleeper_spec.rb +197 -0
- data/spec/sleeper.rb +197 -0
- data/spec/waiter_spec.rb +181 -0
- metadata +16 -14
- data/lib/quack_concurrency/queue/error.rb +0 -6
- data/lib/quack_concurrency/reentrant_mutex/error.rb +0 -6
- data/lib/quack_concurrency/semaphore.rb +0 -139
- data/lib/quack_concurrency/semaphore/error.rb +0 -6
- data/lib/quack_concurrency/uninterruptible_condition_variable.rb +0 -94
- data/lib/quack_concurrency/uninterruptible_sleeper.rb +0 -81
- data/lib/quack_concurrency/yielder.rb +0 -35
- data/spec/semaphore_spec.rb +0 -244
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'quack_concurrency'
|
2
|
+
|
3
|
+
describe QuackConcurrency::SafeConditionVariable do
|
4
|
+
|
5
|
+
it "should inherit ConditionVariable" do
|
6
|
+
expect(described_class).to be < QuackConcurrency::ConditionVariable
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "#signal" do
|
10
|
+
|
11
|
+
context "when called with waiting threads" do
|
12
|
+
it "should resume the next thread currently waiting" do
|
13
|
+
condition_variable = QuackConcurrency::SafeConditionVariable.new
|
14
|
+
mutex = Mutex.new
|
15
|
+
values = []
|
16
|
+
Thread.new { mutex.synchronize { condition_variable.wait(mutex); values << 1 } }
|
17
|
+
Thread.new { sleep 1; mutex.synchronize { condition_variable.wait(mutex); values << 2 } }
|
18
|
+
Thread.new { sleep 2; mutex.synchronize { condition_variable.wait(mutex); values << 3 } }
|
19
|
+
sleep 3
|
20
|
+
condition_variable.signal
|
21
|
+
condition_variable.signal
|
22
|
+
condition_variable.signal
|
23
|
+
sleep 1
|
24
|
+
expect(values).to eq [1, 2, 3]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#wait" do
|
31
|
+
|
32
|
+
context "when called without a timeout" do
|
33
|
+
it "should block until #broadcast or #signal are called" do
|
34
|
+
condition_variable = QuackConcurrency::SafeConditionVariable.new
|
35
|
+
mutex = Mutex.new
|
36
|
+
thread1 = Thread.new { mutex.synchronize { condition_variable.wait(mutex) } }
|
37
|
+
thread2 = Thread.new { mutex.synchronize { condition_variable.wait(mutex) } }
|
38
|
+
sleep 1
|
39
|
+
expect(thread1.alive?).to be true
|
40
|
+
expect(thread2.alive?).to be true
|
41
|
+
condition_variable.broadcast
|
42
|
+
sleep 1
|
43
|
+
expect(thread1.alive?).to be false
|
44
|
+
expect(thread2.alive?).to be false
|
45
|
+
end
|
46
|
+
context "and before Thread#run" do
|
47
|
+
it "should return only after #broadcast or #signal are called" do
|
48
|
+
condition_variable = QuackConcurrency::SafeConditionVariable.new
|
49
|
+
mutex = Mutex.new
|
50
|
+
thread = Thread.new { mutex.synchronize { condition_variable.wait(mutex) } }
|
51
|
+
sleep 1
|
52
|
+
thread.run
|
53
|
+
sleep 1
|
54
|
+
expect(thread.alive?).to be true
|
55
|
+
condition_variable.broadcast
|
56
|
+
sleep 1
|
57
|
+
expect(thread.alive?).to be false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "when called with a timeout" do
|
63
|
+
context "of nil" do
|
64
|
+
it "should return only after #broadcast or #signal are called" do
|
65
|
+
condition_variable = QuackConcurrency::SafeConditionVariable.new
|
66
|
+
mutex = Mutex.new
|
67
|
+
thread = Thread.new { mutex.synchronize { condition_variable.wait(mutex) } }
|
68
|
+
sleep 1
|
69
|
+
expect(thread.alive?).to be true
|
70
|
+
condition_variable.broadcast
|
71
|
+
sleep 1
|
72
|
+
expect(thread.alive?).to be false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
context "of Float::INFINITY" do
|
76
|
+
it "should return only after #broadcast or #signal are called" do
|
77
|
+
condition_variable = QuackConcurrency::SafeConditionVariable.new
|
78
|
+
mutex = Mutex.new
|
79
|
+
thread = Thread.new { mutex.synchronize { condition_variable.wait(mutex) } }
|
80
|
+
sleep 1
|
81
|
+
expect(thread.alive?).to be true
|
82
|
+
condition_variable.broadcast
|
83
|
+
sleep 1
|
84
|
+
expect(thread.alive?).to be false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
context "of positive Integer" do
|
88
|
+
it "should block until timeout reached" do
|
89
|
+
condition_variable = QuackConcurrency::SafeConditionVariable.new
|
90
|
+
mutex = Mutex.new
|
91
|
+
thread = Thread.new { mutex.synchronize { condition_variable.wait(mutex, 2) } }
|
92
|
+
sleep 1
|
93
|
+
expect(thread.alive?).to be true
|
94
|
+
sleep 2
|
95
|
+
expect(thread.alive?).to be false
|
96
|
+
end
|
97
|
+
end
|
98
|
+
context "and Thread#run is called before timeout is reached" do
|
99
|
+
it "should return only after timeout is reached" do
|
100
|
+
condition_variable = QuackConcurrency::SafeConditionVariable.new
|
101
|
+
mutex = Mutex.new
|
102
|
+
thread = Thread.new { mutex.synchronize { condition_variable.wait(mutex, 3) } }
|
103
|
+
sleep 1
|
104
|
+
thread.run
|
105
|
+
sleep 1
|
106
|
+
expect(thread.alive?).to be true
|
107
|
+
sleep 2
|
108
|
+
expect(thread.alive?).to be false
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'quack_concurrency'
|
2
|
+
|
3
|
+
describe QuackConcurrency::SafeSleeper do
|
4
|
+
|
5
|
+
def timer(&block)
|
6
|
+
start_time = Time.now
|
7
|
+
yield(start_time)
|
8
|
+
time_elapsed = Time.now - start_time
|
9
|
+
end
|
10
|
+
|
11
|
+
def delay(units = 1)
|
12
|
+
sleep units
|
13
|
+
end
|
14
|
+
|
15
|
+
def expect_time(actual_time, expected_time)
|
16
|
+
expect(actual_time).to be_between(expected_time - 0.5, expected_time + 0.5)
|
17
|
+
end
|
18
|
+
|
19
|
+
def sleep_test(sleep_duration: , timeout: false, wake_at: nil, sleep_at: 0)
|
20
|
+
sleeper = QuackConcurrency::SafeSleeper.new
|
21
|
+
elapsed_time = nil
|
22
|
+
sleep_thread = Thread.new do
|
23
|
+
delay(sleep_at)
|
24
|
+
if timeout == false
|
25
|
+
elapsed_time = timer { sleeper.sleep }
|
26
|
+
else
|
27
|
+
elapsed_time = timer { sleeper.sleep(timeout) }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
wake_thread = Thread.new do
|
31
|
+
if wake_at
|
32
|
+
delay(wake_at)
|
33
|
+
sleeper.wake
|
34
|
+
end
|
35
|
+
end
|
36
|
+
sleep_thread.join
|
37
|
+
wake_thread.join
|
38
|
+
expect_time(elapsed_time, sleep_duration) # test duration of sleep
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "::new" do
|
42
|
+
|
43
|
+
context "when called with no arguments" do
|
44
|
+
sleeper = nil
|
45
|
+
it "should not raise error" do
|
46
|
+
expect{ sleeper = QuackConcurrency::SafeSleeper.new }.not_to raise_error
|
47
|
+
end
|
48
|
+
it "should return a SafeSleeper" do
|
49
|
+
expect(sleeper).to be_a(QuackConcurrency::SafeSleeper)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#wake" do
|
56
|
+
|
57
|
+
context "when called" do
|
58
|
+
it "should not raise error" do
|
59
|
+
sleeper = QuackConcurrency::SafeSleeper.new
|
60
|
+
expect{ sleeper.wake }.not_to raise_error
|
61
|
+
end
|
62
|
+
it "should wake a sleeping thread immediately if #sleep has already been called" do
|
63
|
+
sleep_test(sleep_duration: 1, wake_at: 1, sleep_at: 0)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when called a second time" do
|
68
|
+
it "should raise RuntimeError" do
|
69
|
+
sleeper = QuackConcurrency::SafeSleeper.new
|
70
|
+
sleeper.wake
|
71
|
+
expect{ sleeper.wake }.to raise_error(RuntimeError)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#sleep" do
|
78
|
+
|
79
|
+
context "when called" do
|
80
|
+
it "should return sleep duration as a Float" do
|
81
|
+
sleeper = QuackConcurrency::SafeSleeper.new
|
82
|
+
return_value = sleeper.sleep(1)
|
83
|
+
expect(return_value).to be_a(Float)
|
84
|
+
expect_time(return_value, 1)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "when called after #wake" do
|
89
|
+
it "should return immediately" do
|
90
|
+
sleep_test(sleep_duration: 0, wake_at: 0, sleep_at: 1)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "when called before #wake" do
|
95
|
+
it "should return after #wake is called" do
|
96
|
+
sleep_test(sleep_duration: 1, wake_at: 1, sleep_at: 0)
|
97
|
+
end
|
98
|
+
context "and #wake gets called before timeout is reached" do
|
99
|
+
it "should return when #wake is called" do
|
100
|
+
sleep_test(sleep_duration: 1, timeout: 2, wake_at: 1, sleep_at: 0)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
context "and #wake gets called after timeout is reached" do
|
104
|
+
it "should return when timeout is reached" do
|
105
|
+
sleep_test(sleep_duration: 1, timeout: 1, wake_at: 2, sleep_at: 0)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context "when called with timeout" do
|
111
|
+
context "of nil" do
|
112
|
+
it "should return only after #wake is called" do
|
113
|
+
sleep_test(sleep_duration: 1, timeout: nil, wake_at: 1, sleep_at: 0)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
context "of Float::INFINITY" do
|
117
|
+
it "should return only after #wake is called" do
|
118
|
+
sleep_test(sleep_duration: 1, timeout: Float::INFINITY, wake_at: 1, sleep_at: 0)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
context "of non Numeric value" do
|
122
|
+
it "should raise TypeError" do
|
123
|
+
sleeper = QuackConcurrency::SafeSleeper.new
|
124
|
+
expect{ sleeper.sleep('1') }.to raise_error(TypeError)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
context "of negative Numeric value" do
|
128
|
+
it "should raise ArgumentError" do
|
129
|
+
sleeper = QuackConcurrency::SafeSleeper.new
|
130
|
+
expect{ sleeper.sleep(-1) }.to raise_error(ArgumentError)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
context "of Numeric value" do
|
134
|
+
it "should return after given timeout" do
|
135
|
+
sleep_test(sleep_duration: 1, timeout: 1, sleep_at: 0)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
context "and Thread#run is called before timeout is reached" do
|
139
|
+
it "should return only after timeout is reached" do
|
140
|
+
sleeper = QuackConcurrency::SafeSleeper.new
|
141
|
+
elapsed_time = nil
|
142
|
+
sleep_thread = Thread.new do
|
143
|
+
elapsed_time = timer { sleeper.sleep(2) }
|
144
|
+
end
|
145
|
+
run_thread = Thread.new do
|
146
|
+
delay 1
|
147
|
+
sleep_thread.run
|
148
|
+
end
|
149
|
+
sleep_thread.join
|
150
|
+
run_thread.join
|
151
|
+
expect_time(elapsed_time, 2)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context "when called before Thread#run" do
|
157
|
+
it "should return only after #wake is called" do
|
158
|
+
sleeper = QuackConcurrency::SafeSleeper.new
|
159
|
+
elapsed_time = nil
|
160
|
+
sleep_thread = Thread.new do
|
161
|
+
elapsed_time = timer { sleeper.sleep }
|
162
|
+
end
|
163
|
+
wake_thread = Thread.new do
|
164
|
+
delay 2
|
165
|
+
sleeper.wake
|
166
|
+
end
|
167
|
+
run_thread = Thread.new do
|
168
|
+
delay 1
|
169
|
+
sleep_thread.run
|
170
|
+
end
|
171
|
+
sleep_thread.join
|
172
|
+
wake_thread.join
|
173
|
+
run_thread.join
|
174
|
+
expect_time(elapsed_time, 2)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context "when called a second time" do
|
179
|
+
it "should raise RuntimeError" do
|
180
|
+
sleeper = QuackConcurrency::SafeSleeper.new
|
181
|
+
sleeper.wake
|
182
|
+
sleeper.sleep
|
183
|
+
expect{ sleeper.sleep }.to raise_error(RuntimeError)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context "when called with no way to wake up" do
|
188
|
+
it "should raise fatal" do
|
189
|
+
sleeper = QuackConcurrency::SafeSleeper.new
|
190
|
+
FatalError = ObjectSpace.each_object(Class).find { |klass| klass < Exception && klass.inspect == 'fatal' }
|
191
|
+
expect{ sleeper.sleep }.to raise_error(FatalError)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
data/spec/sleeper.rb
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'quack_concurrency'
|
2
|
+
|
3
|
+
describe QuackConcurrency::Sleeper do
|
4
|
+
|
5
|
+
def timer(&block)
|
6
|
+
start_time = Time.now
|
7
|
+
yield(start_time)
|
8
|
+
time_elapsed = Time.now - start_time
|
9
|
+
end
|
10
|
+
|
11
|
+
def delay(units = 1)
|
12
|
+
sleep units
|
13
|
+
end
|
14
|
+
|
15
|
+
def expect_time(actual_time, expected_time)
|
16
|
+
expect(actual_time).to be_between(expected_time - 0.5, expected_time + 0.5)
|
17
|
+
end
|
18
|
+
|
19
|
+
def sleep_test(sleep_duration: , timeout: false, wake_at: nil, sleep_at: 0)
|
20
|
+
sleeper = QuackConcurrency::Sleeper.new
|
21
|
+
elapsed_time = nil
|
22
|
+
sleep_thread = Thread.new do
|
23
|
+
delay(sleep_at)
|
24
|
+
if timeout == false
|
25
|
+
elapsed_time = timer { sleeper.sleep }
|
26
|
+
else
|
27
|
+
elapsed_time = timer { sleeper.sleep(timeout) }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
wake_thread = Thread.new do
|
31
|
+
if wake_at
|
32
|
+
delay(wake_at)
|
33
|
+
sleeper.wake
|
34
|
+
end
|
35
|
+
end
|
36
|
+
sleep_thread.join
|
37
|
+
wake_thread.join
|
38
|
+
expect_time(elapsed_time, sleep_duration) # test duration of sleep
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "::new" do
|
42
|
+
|
43
|
+
context "when called with no arguments" do
|
44
|
+
sleeper = nil
|
45
|
+
it "should not raise error" do
|
46
|
+
expect{ sleeper = QuackConcurrency::Sleeper.new }.not_to raise_error
|
47
|
+
end
|
48
|
+
it "should return a Sleeper" do
|
49
|
+
expect(sleeper).to be_a(QuackConcurrency::Sleeper)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#wake" do
|
56
|
+
|
57
|
+
context "when called" do
|
58
|
+
it "should not raise error" do
|
59
|
+
sleeper = QuackConcurrency::Sleeper.new
|
60
|
+
expect{ sleeper.wake }.not_to raise_error
|
61
|
+
end
|
62
|
+
it "should wake a sleeping thread immediately if #sleep has already been called" do
|
63
|
+
sleep_test(sleep_duration: 1, wake_at: 1, sleep_at: 0)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when called a second time" do
|
68
|
+
it "should raise RuntimeError" do
|
69
|
+
sleeper = QuackConcurrency::Sleeper.new
|
70
|
+
sleeper.wake
|
71
|
+
expect{ sleeper.wake }.to raise_error(RuntimeError)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#sleep" do
|
78
|
+
|
79
|
+
context "when called" do
|
80
|
+
it "should return sleep duration as a Float" do
|
81
|
+
sleeper = QuackConcurrency::Sleeper.new
|
82
|
+
return_value = sleeper.sleep(1)
|
83
|
+
expect(return_value).to be_a(Float)
|
84
|
+
expect_time(return_value, 1)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "when called after #wake" do
|
89
|
+
it "should return immediately" do
|
90
|
+
sleep_test(sleep_duration: 0, wake_at: 0, sleep_at: 1)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "when called before #wake" do
|
95
|
+
it "should return after #wake is called" do
|
96
|
+
sleep_test(sleep_duration: 1, wake_at: 1, sleep_at: 0)
|
97
|
+
end
|
98
|
+
context "and #wake gets called before timeout is reached" do
|
99
|
+
it "should return when #wake is called" do
|
100
|
+
sleep_test(sleep_duration: 1, timeout: 2, wake_at: 1, sleep_at: 0)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
context "and #wake gets called after timeout is reached" do
|
104
|
+
it "should return when timeout is reached" do
|
105
|
+
sleep_test(sleep_duration: 1, timeout: 1, wake_at: 2, sleep_at: 0)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context "when called with timeout" do
|
111
|
+
context "of nil" do
|
112
|
+
it "should return only after #wake is called" do
|
113
|
+
sleep_test(sleep_duration: 1, timeout: nil, wake_at: 1, sleep_at: 0)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
context "of Float::INFINITY" do
|
117
|
+
it "should return only after #wake is called" do
|
118
|
+
sleep_test(sleep_duration: 1, timeout: Float::INFINITY, wake_at: 1, sleep_at: 0)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
context "of non Numeric value" do
|
122
|
+
it "should raise TypeError" do
|
123
|
+
sleeper = QuackConcurrency::Sleeper.new
|
124
|
+
expect{ sleeper.sleep('1') }.to raise_error(TypeError)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
context "of negative Numeric value" do
|
128
|
+
it "should raise ArgumentError" do
|
129
|
+
sleeper = QuackConcurrency::Sleeper.new
|
130
|
+
expect{ sleeper.sleep(-1) }.to raise_error(ArgumentError)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
context "of Numeric value" do
|
134
|
+
it "should return after given timeout" do
|
135
|
+
sleep_test(sleep_duration: 1, timeout: 1, sleep_at: 0)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
context "and Thread#run is called before timeout is reached" do
|
139
|
+
it "should return when after Thread#run is called" do
|
140
|
+
sleeper = QuackConcurrency::Sleeper.new
|
141
|
+
elapsed_time = nil
|
142
|
+
sleep_thread = Thread.new do
|
143
|
+
elapsed_time = timer { sleeper.sleep(2) }
|
144
|
+
end
|
145
|
+
run_thread = Thread.new do
|
146
|
+
delay 1
|
147
|
+
sleep_thread.run
|
148
|
+
end
|
149
|
+
sleep_thread.join
|
150
|
+
run_thread.join
|
151
|
+
expect_time(elapsed_time, 1)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context "when called before Thread#run" do
|
157
|
+
it "should return when after Thread#run is called" do
|
158
|
+
sleeper = QuackConcurrency::Sleeper.new
|
159
|
+
elapsed_time = nil
|
160
|
+
sleep_thread = Thread.new do
|
161
|
+
elapsed_time = timer { sleeper.sleep }
|
162
|
+
end
|
163
|
+
wake_thread = Thread.new do
|
164
|
+
delay 2
|
165
|
+
sleeper.wake
|
166
|
+
end
|
167
|
+
run_thread = Thread.new do
|
168
|
+
delay 1
|
169
|
+
sleep_thread.run
|
170
|
+
end
|
171
|
+
sleep_thread.join
|
172
|
+
wake_thread.join
|
173
|
+
run_thread.join
|
174
|
+
expect_time(elapsed_time, 1)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context "when called a second time" do
|
179
|
+
it "should raise RuntimeError" do
|
180
|
+
sleeper = QuackConcurrency::Sleeper.new
|
181
|
+
sleeper.wake
|
182
|
+
sleeper.sleep
|
183
|
+
expect{ sleeper.sleep }.to raise_error(RuntimeError)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context "when called with no way to wake up" do
|
188
|
+
it "should raise fatal" do
|
189
|
+
sleeper = QuackConcurrency::Sleeper.new
|
190
|
+
FatalError = ObjectSpace.each_object(Class).find { |klass| klass < Exception && klass.inspect == 'fatal' }
|
191
|
+
expect{ sleeper.sleep }.to raise_error(FatalError)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|