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
data/spec/future_spec.rb
CHANGED
@@ -1,120 +1,142 @@
|
|
1
1
|
require 'quack_concurrency'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
describe "
|
6
|
-
|
7
|
-
context "when called" do
|
8
|
-
it "should
|
9
|
-
|
10
|
-
expect
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
context "when called a second time" do
|
15
|
-
it "should raise QuackConcurrency::Future::Complete" do
|
16
|
-
future = QuackConcurrency::Future.new
|
17
|
-
future.set(1)
|
18
|
-
expect{ future.set(2) }.to raise_error(QuackConcurrency::Future::Complete)
|
3
|
+
describe QuackConcurrency::Future do
|
4
|
+
|
5
|
+
describe "::new" do
|
6
|
+
|
7
|
+
context "when called with no arguments" do
|
8
|
+
it "should return a Mutex" do
|
9
|
+
mutex = QuackConcurrency::Mutex.new
|
10
|
+
expect(mutex).to be_a(QuackConcurrency::Mutex)
|
19
11
|
end
|
20
12
|
end
|
21
|
-
|
13
|
+
|
22
14
|
end
|
23
|
-
|
15
|
+
|
24
16
|
describe "#cancel" do
|
25
|
-
|
17
|
+
|
26
18
|
context "when called" do
|
27
19
|
it "should not raise error" do
|
28
20
|
future = QuackConcurrency::Future.new
|
29
21
|
expect{ future.cancel }.not_to raise_error
|
30
22
|
end
|
31
23
|
end
|
32
|
-
|
24
|
+
|
33
25
|
context "when called a second time" do
|
34
|
-
it "should raise
|
26
|
+
it "should raise Future::Complete" do
|
35
27
|
future = QuackConcurrency::Future.new
|
36
28
|
future.cancel
|
37
29
|
expect{ future.cancel }.to raise_error(QuackConcurrency::Future::Complete)
|
38
30
|
end
|
39
31
|
end
|
40
|
-
|
32
|
+
|
33
|
+
context "when called" do
|
34
|
+
it "should raise Future::Canceled when get called" do
|
35
|
+
future = QuackConcurrency::Future.new
|
36
|
+
future.cancel
|
37
|
+
expect{ future.get }.to raise_error(QuackConcurrency::Future::Canceled)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
41
|
end
|
42
|
-
|
43
|
-
describe "#
|
44
|
-
|
45
|
-
context "when
|
46
|
-
it "should return
|
42
|
+
|
43
|
+
describe "#complete?" do
|
44
|
+
|
45
|
+
context "when called when no value or error has been set" do
|
46
|
+
it "should return false" do
|
47
|
+
future = QuackConcurrency::Future.new
|
48
|
+
expect(future.complete?).to be false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "when called when a value has been set" do
|
53
|
+
it "should return true" do
|
47
54
|
future = QuackConcurrency::Future.new
|
48
55
|
future.set(1)
|
49
|
-
expect(future.
|
56
|
+
expect(future.complete?).to be true
|
50
57
|
end
|
51
58
|
end
|
52
|
-
|
53
|
-
context "when
|
54
|
-
it "should return
|
59
|
+
|
60
|
+
context "when called when a error has been set" do
|
61
|
+
it "should return true" do
|
62
|
+
future = QuackConcurrency::Future.new
|
63
|
+
future.raise
|
64
|
+
expect(future.complete?).to be true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#get" do
|
71
|
+
|
72
|
+
context "when called after #set" do
|
73
|
+
it "should return value set" do
|
55
74
|
future = QuackConcurrency::Future.new
|
56
75
|
future.set(1)
|
57
|
-
future.get
|
58
76
|
expect(future.get).to eql 1
|
59
77
|
end
|
60
78
|
end
|
61
|
-
|
62
|
-
context "when
|
63
|
-
it "should
|
79
|
+
|
80
|
+
context "when called after #raise" do
|
81
|
+
it "should raise error set" do
|
64
82
|
future = QuackConcurrency::Future.new
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
69
|
-
start_time = Time.now
|
70
|
-
expect(future.get).to eql 1
|
71
|
-
end_time = Time.now
|
72
|
-
duration = end_time - start_time
|
73
|
-
thread.join
|
74
|
-
expect(duration).to be > 0.5
|
83
|
+
e = Class.new(StandardError)
|
84
|
+
future.raise e
|
85
|
+
expect{ future.get }.to raise_error(e)
|
75
86
|
end
|
76
87
|
end
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
describe "#set, #cancel" do
|
81
|
-
|
82
|
-
context "when #set called after #cancel" do
|
83
|
-
it "should raise QuackConcurrency::Future::Complete" do
|
88
|
+
|
89
|
+
context "when called a second time" do
|
90
|
+
it "should return value set again" do
|
84
91
|
future = QuackConcurrency::Future.new
|
85
|
-
future.
|
86
|
-
|
92
|
+
future.set(1)
|
93
|
+
future.get
|
94
|
+
expect(future.get).to eql 1
|
87
95
|
end
|
88
96
|
end
|
89
|
-
|
90
|
-
context "when
|
91
|
-
it "should
|
97
|
+
|
98
|
+
context "when called before #set is" do
|
99
|
+
it "should wait and return value set after #set is called" do
|
92
100
|
future = QuackConcurrency::Future.new
|
101
|
+
value = nil
|
102
|
+
thread = Thread.new { sleep 1; value = future.get }
|
103
|
+
sleep 2
|
104
|
+
expect(thread.alive?).to be true
|
93
105
|
future.set(1)
|
94
|
-
|
106
|
+
sleep 1
|
107
|
+
expect(value).to be 1
|
95
108
|
end
|
96
109
|
end
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
describe "#get, #cancel" do
|
101
|
-
|
102
|
-
context "when #get called after #cancel" do
|
103
|
-
it "should raise QuackConcurrency::Future::Canceled" do
|
110
|
+
|
111
|
+
context "when called before #raise is" do
|
112
|
+
it "should wait and raise error set after #raise is called" do
|
104
113
|
future = QuackConcurrency::Future.new
|
105
|
-
|
106
|
-
|
114
|
+
e_class = Class.new(StandardError)
|
115
|
+
error = nil
|
116
|
+
thread = Thread.new do
|
117
|
+
sleep 1
|
118
|
+
begin
|
119
|
+
future.get
|
120
|
+
rescue e_class => e
|
121
|
+
error = e
|
122
|
+
end
|
123
|
+
end
|
124
|
+
sleep 2
|
125
|
+
expect(thread.alive?).to be true
|
126
|
+
future.raise(e_class)
|
127
|
+
sleep 1
|
128
|
+
expect(error).to be_a e_class
|
107
129
|
end
|
108
130
|
end
|
109
|
-
|
131
|
+
|
110
132
|
end
|
111
133
|
|
112
134
|
describe "#raise" do
|
113
|
-
|
114
|
-
context "when called
|
115
|
-
it "should set the error to
|
135
|
+
|
136
|
+
context "when called without an argument" do
|
137
|
+
it "should set the error to a StandardError" do
|
116
138
|
future = QuackConcurrency::Future.new
|
117
|
-
|
139
|
+
future.raise
|
118
140
|
expect{ future.get }.to raise_error(StandardError)
|
119
141
|
end
|
120
142
|
end
|
@@ -123,7 +145,7 @@ RSpec.describe QuackConcurrency::Future do
|
|
123
145
|
it "should set the error to that instance" do
|
124
146
|
future = QuackConcurrency::Future.new
|
125
147
|
e = TypeError.new
|
126
|
-
|
148
|
+
future.raise(e)
|
127
149
|
expect{ future.get }.to raise_error(e)
|
128
150
|
end
|
129
151
|
end
|
@@ -131,18 +153,62 @@ RSpec.describe QuackConcurrency::Future do
|
|
131
153
|
context "when called with an error class" do
|
132
154
|
it "should set the error to an instance of that class" do
|
133
155
|
future = QuackConcurrency::Future.new
|
134
|
-
|
135
|
-
|
156
|
+
e = Class.new(StandardError)
|
157
|
+
future.raise(e)
|
158
|
+
expect{ future.get }.to raise_error(e)
|
136
159
|
end
|
137
160
|
end
|
138
161
|
|
139
162
|
context "when called with an invalid argument" do
|
140
|
-
it "should raise
|
163
|
+
it "should raise TypeError" do
|
164
|
+
future = QuackConcurrency::Future.new
|
165
|
+
expect{ future.raise("error") }.to raise_error(TypeError)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context "when called when value already set" do
|
170
|
+
it "should raise Future::Complete" do
|
171
|
+
future = QuackConcurrency::Future.new
|
172
|
+
future.set(1)
|
173
|
+
expect{ future.raise }.to raise_error(QuackConcurrency::Future::Complete)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context "when called when error already set" do
|
178
|
+
it "should raise Future::Complete" do
|
141
179
|
future = QuackConcurrency::Future.new
|
142
|
-
|
180
|
+
future.raise
|
181
|
+
expect{ future.raise }.to raise_error(QuackConcurrency::Future::Complete)
|
143
182
|
end
|
144
183
|
end
|
145
|
-
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
describe "#set" do
|
188
|
+
|
189
|
+
context "when called" do
|
190
|
+
it "should not raise error" do
|
191
|
+
future = QuackConcurrency::Future.new
|
192
|
+
expect{ future.set(1) }.not_to raise_error
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
context "when called when value already set" do
|
197
|
+
it "should raise QuackConcurrency::Future::Complete" do
|
198
|
+
future = QuackConcurrency::Future.new
|
199
|
+
future.set(1)
|
200
|
+
expect{ future.set(2) }.to raise_error(QuackConcurrency::Future::Complete)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context "when called when error already set" do
|
205
|
+
it "should raise QuackConcurrency::Future::Complete" do
|
206
|
+
future = QuackConcurrency::Future.new
|
207
|
+
future.raise
|
208
|
+
expect{ future.set(2) }.to raise_error(QuackConcurrency::Future::Complete)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
146
212
|
end
|
147
|
-
|
213
|
+
|
148
214
|
end
|
data/spec/mutex_spec.rb
ADDED
@@ -0,0 +1,441 @@
|
|
1
|
+
require 'quack_concurrency'
|
2
|
+
|
3
|
+
describe QuackConcurrency::Mutex do
|
4
|
+
|
5
|
+
describe "::new" do
|
6
|
+
|
7
|
+
context "when called with no arguments" do
|
8
|
+
it "should return a Mutex" do
|
9
|
+
mutex = QuackConcurrency::Mutex.new
|
10
|
+
expect(mutex).to be_a(QuackConcurrency::Mutex)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#lock" do
|
17
|
+
|
18
|
+
context "when called when Mutex is not locked" do
|
19
|
+
it "should not raise error" do
|
20
|
+
mutex = QuackConcurrency::Mutex.new
|
21
|
+
expect { mutex.lock }.not_to raise_error
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when called when Mutex is locked by this thread" do
|
26
|
+
it "should raise ThreadError" do
|
27
|
+
mutex = QuackConcurrency::Mutex.new
|
28
|
+
mutex.lock
|
29
|
+
expect { mutex.lock }.to raise_error(ThreadError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "when called when another thread is locking the Mutex" do
|
34
|
+
it "should only return after the other thead has released the lock" do
|
35
|
+
mutex = QuackConcurrency::Mutex.new
|
36
|
+
Thread.new { mutex.lock { sleep 3 } }
|
37
|
+
thread = Thread.new { sleep 1; mutex.lock }
|
38
|
+
sleep 2
|
39
|
+
expect(thread.alive?).to be true
|
40
|
+
sleep 2
|
41
|
+
expect(thread.alive?).to be false
|
42
|
+
end
|
43
|
+
context "and another thread waiting on the mutex" do
|
44
|
+
it "should wake the threads in order of calling #lock" do
|
45
|
+
mutex = QuackConcurrency::Mutex.new
|
46
|
+
mutex.lock
|
47
|
+
values = []
|
48
|
+
thread1 = Thread.new { mutex.lock { values << 1 } }
|
49
|
+
thread2 = Thread.new { sleep 1; mutex.lock { values << 2 } }
|
50
|
+
thread3 = Thread.new { sleep 2; mutex.lock { values << 3 } }
|
51
|
+
sleep 3
|
52
|
+
mutex.unlock
|
53
|
+
sleep 1
|
54
|
+
expect(values).to eq [1, 2, 3]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "when called with a block" do
|
60
|
+
it "should run block and return it's value" do
|
61
|
+
mutex = QuackConcurrency::Mutex.new
|
62
|
+
expect(mutex.lock { :a }).to be :a
|
63
|
+
end
|
64
|
+
it "should pass up any error raised in the block" do
|
65
|
+
mutex = QuackConcurrency::Mutex.new
|
66
|
+
e = Class.new(StandardError)
|
67
|
+
expect{ mutex.lock { raise e } }.to raise_error(e)
|
68
|
+
end
|
69
|
+
it "should hold lock while block is executed" do
|
70
|
+
mutex = QuackConcurrency::Mutex.new
|
71
|
+
hold_thread = Thread.new { mutex.lock { sleep 3 } }
|
72
|
+
lock_thread = Thread.new { sleep 1; mutex.lock }
|
73
|
+
sleep 2
|
74
|
+
expect(lock_thread.alive?).to be true
|
75
|
+
hold_thread.join
|
76
|
+
lock_thread.join
|
77
|
+
end
|
78
|
+
it "should release the lock after the block has returned" do
|
79
|
+
mutex = QuackConcurrency::Mutex.new
|
80
|
+
Thread.new { mutex.lock { sleep 2 } }
|
81
|
+
lock_thread = Thread.new { sleep 1; mutex.lock }
|
82
|
+
sleep 3
|
83
|
+
expect(lock_thread.alive?).to be false
|
84
|
+
end
|
85
|
+
context "that raises an error" do
|
86
|
+
it "should release the lock after the block has returned" do
|
87
|
+
mutex = QuackConcurrency::Mutex.new
|
88
|
+
Thread.new do
|
89
|
+
mutex.lock { sleep 2; raise } rescue nil
|
90
|
+
end
|
91
|
+
sleep 3
|
92
|
+
expect(mutex.locked?).to be false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
context "that unlocks the mutex" do
|
96
|
+
it "should raise ThreadError" do
|
97
|
+
mutex = QuackConcurrency::Mutex.new
|
98
|
+
expect{ mutex.lock { mutex.unlock } }.to raise_error(ThreadError)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "#locked?" do
|
106
|
+
|
107
|
+
context "when called when no threads hold the lock" do
|
108
|
+
it "should return false" do
|
109
|
+
mutex = QuackConcurrency::Mutex.new
|
110
|
+
expect(mutex.locked?).to be false
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "when called when a thread holds the lock" do
|
115
|
+
it "should return true" do
|
116
|
+
mutex = QuackConcurrency::Mutex.new
|
117
|
+
thread = Thread.new { mutex.lock { sleep 2 } }
|
118
|
+
sleep 1
|
119
|
+
expect(mutex.locked?).to be true
|
120
|
+
thread.join
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "#locked_out?" do
|
127
|
+
|
128
|
+
context "when called when mutex is locked by another thread" do
|
129
|
+
it "should return true" do
|
130
|
+
mutex = QuackConcurrency::Mutex.new
|
131
|
+
thread = Thread.new { mutex.lock; sleep 2 }
|
132
|
+
sleep 1
|
133
|
+
expect(mutex.locked_out?).to be true
|
134
|
+
thread.join
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context "when called when mutex is locked by this thread" do
|
139
|
+
it "should return false" do
|
140
|
+
mutex = QuackConcurrency::Mutex.new
|
141
|
+
mutex.lock
|
142
|
+
expect(mutex.locked_out?).to be false
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context "when called when mutex is not locked" do
|
147
|
+
it "should return false" do
|
148
|
+
mutex = QuackConcurrency::Mutex.new
|
149
|
+
expect(mutex.locked_out?).to be false
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "#owned?" do
|
156
|
+
|
157
|
+
context "when called when no threads hold the lock" do
|
158
|
+
it "should return false" do
|
159
|
+
mutex = QuackConcurrency::Mutex.new
|
160
|
+
expect(mutex.owned?).to be false
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context "when called when another thread holds the lock" do
|
165
|
+
it "should return false" do
|
166
|
+
mutex = QuackConcurrency::Mutex.new
|
167
|
+
thread = Thread.new { mutex.lock { sleep 2 } }
|
168
|
+
sleep 1
|
169
|
+
expect(mutex.owned?).to be false
|
170
|
+
thread.join
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context "when called when this thread holds the lock" do
|
175
|
+
it "should return true" do
|
176
|
+
mutex = QuackConcurrency::Mutex.new
|
177
|
+
mutex.lock
|
178
|
+
expect(mutex.owned?).to be true
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
describe "#owner" do
|
185
|
+
|
186
|
+
context "when called when no threads hold the lock" do
|
187
|
+
it "should return nil" do
|
188
|
+
mutex = QuackConcurrency::Mutex.new
|
189
|
+
expect(mutex.owner).to be nil
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
context "when called when a thread holds the lock" do
|
194
|
+
it "should return the Thread" do
|
195
|
+
mutex = QuackConcurrency::Mutex.new
|
196
|
+
thread = Thread.new { mutex.lock { sleep 2 } }
|
197
|
+
sleep 1
|
198
|
+
expect(mutex.owner).to be thread
|
199
|
+
thread.join
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
describe "#sleep" do
|
206
|
+
|
207
|
+
context "when called while not locking the Mutex" do
|
208
|
+
it "should raise ThreadError" do
|
209
|
+
mutex = QuackConcurrency::Mutex.new
|
210
|
+
expect{ mutex.sleep }.to raise_error(ThreadError)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
context "when called" do
|
215
|
+
it "should relock the Mutex after thread is woken" do
|
216
|
+
mutex = QuackConcurrency::Mutex.new
|
217
|
+
thread = Thread.new { mutex.lock { mutex.sleep; sleep 2 } }
|
218
|
+
sleep 1
|
219
|
+
thread.run
|
220
|
+
sleep 1
|
221
|
+
expect(mutex.locked?).to be true
|
222
|
+
thread.join
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
context "when called with no timeout" do
|
227
|
+
it "should return only after Thread#run is called" do
|
228
|
+
mutex = QuackConcurrency::Mutex.new
|
229
|
+
thread = Thread.new { mutex.lock { mutex.sleep } }
|
230
|
+
sleep 1
|
231
|
+
expect(thread.alive?).to be true
|
232
|
+
thread.run
|
233
|
+
sleep 1
|
234
|
+
expect(thread.alive?).to be false
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
context "when called with a timeout" do
|
239
|
+
it "should return timeout reached" do
|
240
|
+
mutex = QuackConcurrency::Mutex.new
|
241
|
+
thread = Thread.new { mutex.lock { mutex.sleep(1) } }
|
242
|
+
sleep 2
|
243
|
+
expect(thread.alive?).to be false
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
248
|
+
|
249
|
+
describe "#synchronize" do
|
250
|
+
|
251
|
+
context "when called without a block" do
|
252
|
+
it "should raise ThreadError" do
|
253
|
+
mutex = QuackConcurrency::Mutex.new
|
254
|
+
expect{ mutex.synchronize }.to raise_error(ThreadError)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
context "when called when another thread is locking the Mutex" do
|
259
|
+
it "should only return after the other thead has released the lock" do
|
260
|
+
mutex = QuackConcurrency::Mutex.new
|
261
|
+
Thread.new { mutex.lock { sleep 3 } }
|
262
|
+
thread = Thread.new { sleep 1; mutex.synchronize {} }
|
263
|
+
sleep 2
|
264
|
+
expect(thread.alive?).to be true
|
265
|
+
sleep 2
|
266
|
+
expect(thread.alive?).to be false
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
context "when called with a block" do
|
271
|
+
it "should run block and return it's value" do
|
272
|
+
mutex = QuackConcurrency::Mutex.new
|
273
|
+
expect(mutex.synchronize { :a }).to be :a
|
274
|
+
end
|
275
|
+
it "should hold lock while block is executed" do
|
276
|
+
mutex = QuackConcurrency::Mutex.new
|
277
|
+
hold_thread = Thread.new { mutex.synchronize { sleep 3 } }
|
278
|
+
lock_thread = Thread.new { sleep 1; mutex.lock }
|
279
|
+
sleep 2
|
280
|
+
expect(lock_thread.alive?).to be true
|
281
|
+
hold_thread.join
|
282
|
+
end
|
283
|
+
it "should release the lock after the block has returned" do
|
284
|
+
mutex = QuackConcurrency::Mutex.new
|
285
|
+
Thread.new { mutex.synchronize { sleep 2 } }
|
286
|
+
lock_thread = Thread.new { sleep 1; mutex.lock }
|
287
|
+
sleep 3
|
288
|
+
expect(lock_thread.alive?).to be false
|
289
|
+
end
|
290
|
+
context "that raises an error" do
|
291
|
+
it "should release the lock after the block has returned" do
|
292
|
+
mutex = QuackConcurrency::Mutex.new
|
293
|
+
Thread.new do
|
294
|
+
mutex.synchronize { sleep 2; raise } rescue nil
|
295
|
+
end
|
296
|
+
lock_thread = Thread.new { sleep 1; mutex.lock }
|
297
|
+
sleep 3
|
298
|
+
expect(lock_thread.alive?).to be false
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
end
|
304
|
+
|
305
|
+
describe "#try_lock" do
|
306
|
+
|
307
|
+
context "when called when no threads locking the Mutex" do
|
308
|
+
it "should reutrn true" do
|
309
|
+
mutex = QuackConcurrency::Mutex.new
|
310
|
+
expect(mutex.try_lock).to eql true
|
311
|
+
end
|
312
|
+
end
|
313
|
+
context "when called when a thread is locking the Mutex" do
|
314
|
+
it "should reutrn true" do
|
315
|
+
mutex = QuackConcurrency::Mutex.new
|
316
|
+
thread = Thread.new { mutex.lock; sleep 2 }
|
317
|
+
sleep 1
|
318
|
+
expect(mutex.try_lock).to eql false
|
319
|
+
thread.join
|
320
|
+
end
|
321
|
+
end
|
322
|
+
context "when called when this thread is locking the Mutex" do
|
323
|
+
it "should raise ThreadError" do
|
324
|
+
mutex = QuackConcurrency::Mutex.new
|
325
|
+
mutex.lock
|
326
|
+
expect{mutex.try_lock}.to raise_error(ThreadError)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
end
|
331
|
+
|
332
|
+
describe "#unlock" do
|
333
|
+
|
334
|
+
context "when called when locking the Mutex" do
|
335
|
+
it "should release the lock" do
|
336
|
+
mutex = QuackConcurrency::Mutex.new
|
337
|
+
mutex.lock
|
338
|
+
thread = Thread.new { mutex.lock }
|
339
|
+
sleep 1
|
340
|
+
expect(thread.alive?).to be true
|
341
|
+
mutex.unlock
|
342
|
+
sleep 1
|
343
|
+
expect(thread.alive?).to be false
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
context "when called when not locking the Mutex" do
|
348
|
+
it "should raise ThreadError" do
|
349
|
+
mutex = QuackConcurrency::Mutex.new
|
350
|
+
expect { mutex.unlock }.to raise_error(ThreadError)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
context "when called with a block" do
|
355
|
+
it "should run block and return it's value" do
|
356
|
+
mutex = QuackConcurrency::Mutex.new
|
357
|
+
mutex.lock
|
358
|
+
expect(mutex.unlock { :a }).to be :a
|
359
|
+
end
|
360
|
+
it "should pass up any error raised in the block" do
|
361
|
+
mutex = QuackConcurrency::Mutex.new
|
362
|
+
e = Class.new(StandardError)
|
363
|
+
mutex.lock
|
364
|
+
expect{ mutex.unlock { raise e } }.to raise_error(e)
|
365
|
+
end
|
366
|
+
it "should release the lock while block is executed" do
|
367
|
+
mutex = QuackConcurrency::Mutex.new
|
368
|
+
thread = Thread.new do
|
369
|
+
mutex.lock { mutex.unlock { sleep 2 } }
|
370
|
+
end
|
371
|
+
sleep 1
|
372
|
+
expect(mutex.locked?).to be false
|
373
|
+
thread.join
|
374
|
+
end
|
375
|
+
it "should reacquire the lock after the block has returned" do
|
376
|
+
mutex = QuackConcurrency::Mutex.new
|
377
|
+
thread = Thread.new do
|
378
|
+
mutex.lock { mutex.unlock {}; sleep 2 }
|
379
|
+
end
|
380
|
+
sleep 1
|
381
|
+
expect(mutex.locked?).to be true
|
382
|
+
thread.join
|
383
|
+
end
|
384
|
+
context "that raises an error when no other thread is locking the Mutex" do
|
385
|
+
it "should reacquire the lock after the block has returned" do
|
386
|
+
mutex = QuackConcurrency::Mutex.new
|
387
|
+
thread = Thread.new do
|
388
|
+
mutex.lock do
|
389
|
+
mutex.unlock { raise } rescue nil
|
390
|
+
sleep 2
|
391
|
+
end
|
392
|
+
end
|
393
|
+
sleep 1
|
394
|
+
expect(mutex.locked?).to be true
|
395
|
+
thread.join
|
396
|
+
end
|
397
|
+
end
|
398
|
+
context "that raises an error when another thread is locking the Mutex" do
|
399
|
+
it "should raise ThreadError after the block has returned" do
|
400
|
+
mutex = QuackConcurrency::Mutex.new
|
401
|
+
thread = Thread.new { sleep 1; mutex.lock; sleep 2 }
|
402
|
+
mutex.lock
|
403
|
+
expect{ mutex.unlock { sleep 2; raise } }.to raise_error(ThreadError)
|
404
|
+
thread.join
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
end
|
410
|
+
|
411
|
+
describe "#waiting_threads_count" do
|
412
|
+
|
413
|
+
context "when called" do
|
414
|
+
it "should return a Integer" do
|
415
|
+
mutex = QuackConcurrency::Mutex.new
|
416
|
+
expect(mutex.waiting_threads_count).to be_a(Integer)
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
context "when called with no waiting threads" do
|
421
|
+
it "should return 0" do
|
422
|
+
mutex = QuackConcurrency::Mutex.new
|
423
|
+
expect(mutex.waiting_threads_count).to eq(0)
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
context "when called with one waiting thread" do
|
428
|
+
it "should return 1" do
|
429
|
+
mutex = QuackConcurrency::Mutex.new
|
430
|
+
mutex.lock
|
431
|
+
thread = Thread.new { mutex.lock }
|
432
|
+
sleep 1
|
433
|
+
expect(mutex.waiting_threads_count).to eq(1)
|
434
|
+
mutex.unlock
|
435
|
+
thread.join
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
end
|
440
|
+
|
441
|
+
end
|