quack_concurrency 0.5.4 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/quack_concurrency.rb +6 -13
  4. data/lib/quack_concurrency/condition_variable.rb +91 -85
  5. data/lib/quack_concurrency/condition_variable/waitable.rb +108 -0
  6. data/lib/quack_concurrency/error.rb +1 -1
  7. data/lib/quack_concurrency/future.rb +31 -30
  8. data/lib/quack_concurrency/future/canceled.rb +1 -0
  9. data/lib/quack_concurrency/future/complete.rb +1 -0
  10. data/lib/quack_concurrency/mutex.rb +140 -38
  11. data/lib/quack_concurrency/queue.rb +32 -28
  12. data/lib/quack_concurrency/reentrant_mutex.rb +64 -76
  13. data/lib/quack_concurrency/safe_condition_variable.rb +23 -0
  14. data/lib/quack_concurrency/safe_condition_variable/waitable.rb +21 -0
  15. data/lib/quack_concurrency/safe_sleeper.rb +80 -0
  16. data/lib/quack_concurrency/sleeper.rb +100 -0
  17. data/lib/quack_concurrency/waiter.rb +32 -23
  18. data/spec/condition_variable_spec.rb +216 -0
  19. data/spec/future_spec.rb +145 -79
  20. data/spec/mutex_spec.rb +441 -0
  21. data/spec/queue_spec.rb +217 -77
  22. data/spec/reentrant_mutex_spec.rb +394 -99
  23. data/spec/safe_condition_variable_spec.rb +115 -0
  24. data/spec/safe_sleeper_spec.rb +197 -0
  25. data/spec/sleeper.rb +197 -0
  26. data/spec/waiter_spec.rb +181 -0
  27. metadata +16 -14
  28. data/lib/quack_concurrency/queue/error.rb +0 -6
  29. data/lib/quack_concurrency/reentrant_mutex/error.rb +0 -6
  30. data/lib/quack_concurrency/semaphore.rb +0 -139
  31. data/lib/quack_concurrency/semaphore/error.rb +0 -6
  32. data/lib/quack_concurrency/uninterruptible_condition_variable.rb +0 -94
  33. data/lib/quack_concurrency/uninterruptible_sleeper.rb +0 -81
  34. data/lib/quack_concurrency/yielder.rb +0 -35
  35. data/spec/semaphore_spec.rb +0 -244
@@ -1,120 +1,142 @@
1
1
  require 'quack_concurrency'
2
2
 
3
- RSpec.describe QuackConcurrency::Future do
4
-
5
- describe "#set" do
6
-
7
- context "when called" do
8
- it "should not raise error" do
9
- future = QuackConcurrency::Future.new
10
- expect{ future.set(1) }.not_to raise_error
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 QuackConcurrency::Future::Complete" do
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 "#set, #get" do
44
-
45
- context "when #get called after #set" do
46
- it "should return value from #set argument" do
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.get).to eql 1
56
+ expect(future.complete?).to be true
50
57
  end
51
58
  end
52
-
53
- context "when #get called a second time" do
54
- it "should return value from #set argument" do
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 #get called before #set" do
63
- it "should wait and return value from #set argument after #set is called" do
79
+
80
+ context "when called after #raise" do
81
+ it "should raise error set" do
64
82
  future = QuackConcurrency::Future.new
65
- thread = Thread.new do
66
- sleep 1
67
- future.set(1)
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
- end
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.cancel
86
- expect{ future.set(1) }.to raise_error(QuackConcurrency::Future::Complete)
92
+ future.set(1)
93
+ future.get
94
+ expect(future.get).to eql 1
87
95
  end
88
96
  end
89
-
90
- context "when #cancel called after #set" do
91
- it "should raise QuackConcurrency::Future::Complete" do
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
- expect{ future.cancel }.to raise_error(QuackConcurrency::Future::Complete)
106
+ sleep 1
107
+ expect(value).to be 1
95
108
  end
96
109
  end
97
-
98
- end
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
- future.cancel
106
- expect{ future.get }.to raise_error(QuackConcurrency::Future::Canceled)
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 with nil" do
115
- it "should set the error to raise StandardError" do
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
- expect{ future.raise }.not_to raise_error
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
- expect{ future.raise(e) }.not_to raise_error
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
- expect{ future.raise(TypeError) }.not_to raise_error
135
- expect{ future.get }.to raise_error(TypeError)
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 ArgumentError" do
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
- expect{ future.raise("error") }.to raise_error(ArgumentError)
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
@@ -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