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.
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