1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.google.common.util.concurrent;
18
19 import com.google.common.base.Joiner;
20 import com.google.common.util.concurrent.CycleDetectingLockFactory.Policies;
21 import com.google.common.util.concurrent.CycleDetectingLockFactory.Policy;
22 import com.google.common.util.concurrent.CycleDetectingLockFactory.PotentialDeadlockException;
23
24 import junit.framework.TestCase;
25
26 import java.util.concurrent.CountDownLatch;
27 import java.util.concurrent.TimeUnit;
28 import java.util.concurrent.locks.Lock;
29 import java.util.concurrent.locks.ReentrantLock;
30 import java.util.concurrent.locks.ReentrantReadWriteLock;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33
34
35
36
37
38
39 public class CycleDetectingLockFactoryTest extends TestCase {
40
41 private ReentrantLock lockA;
42 private ReentrantLock lockB;
43 private ReentrantLock lockC;
44 private ReentrantReadWriteLock.ReadLock readLockA;
45 private ReentrantReadWriteLock.ReadLock readLockB;
46 private ReentrantReadWriteLock.ReadLock readLockC;
47 private ReentrantReadWriteLock.WriteLock writeLockA;
48 private ReentrantReadWriteLock.WriteLock writeLockB;
49 private ReentrantReadWriteLock.WriteLock writeLockC;
50 private ReentrantLock lock1;
51 private ReentrantLock lock2;
52 private ReentrantLock lock3;
53 private ReentrantLock lock01;
54 private ReentrantLock lock02;
55 private ReentrantLock lock03;
56
57 @Override
58 protected void setUp() throws Exception {
59 super.setUp();
60 CycleDetectingLockFactory factory =
61 CycleDetectingLockFactory.newInstance(Policies.THROW);
62 lockA = factory.newReentrantLock("LockA");
63 lockB = factory.newReentrantLock("LockB");
64 lockC = factory.newReentrantLock("LockC");
65 ReentrantReadWriteLock readWriteLockA =
66 factory.newReentrantReadWriteLock("ReadWriteA");
67 ReentrantReadWriteLock readWriteLockB =
68 factory.newReentrantReadWriteLock("ReadWriteB");
69 ReentrantReadWriteLock readWriteLockC =
70 factory.newReentrantReadWriteLock("ReadWriteC");
71 readLockA = readWriteLockA.readLock();
72 readLockB = readWriteLockB.readLock();
73 readLockC = readWriteLockC.readLock();
74 writeLockA = readWriteLockA.writeLock();
75 writeLockB = readWriteLockB.writeLock();
76 writeLockC = readWriteLockC.writeLock();
77
78 CycleDetectingLockFactory.WithExplicitOrdering<MyOrder> factory2 =
79 newInstanceWithExplicitOrdering(MyOrder.class, Policies.THROW);
80 lock1 = factory2.newReentrantLock(MyOrder.FIRST);
81 lock2 = factory2.newReentrantLock(MyOrder.SECOND);
82 lock3 = factory2.newReentrantLock(MyOrder.THIRD);
83
84 CycleDetectingLockFactory.WithExplicitOrdering<OtherOrder> factory3 =
85 newInstanceWithExplicitOrdering(OtherOrder.class, Policies.THROW);
86 lock01 = factory3.newReentrantLock(OtherOrder.FIRST);
87 lock02 = factory3.newReentrantLock(OtherOrder.SECOND);
88 lock03 = factory3.newReentrantLock(OtherOrder.THIRD);
89 }
90
91
92
93
94 private <E extends Enum<E>> CycleDetectingLockFactory.WithExplicitOrdering<E>
95 newInstanceWithExplicitOrdering(Class<E> enumClass, Policy policy) {
96 return new CycleDetectingLockFactory.WithExplicitOrdering<E>(
97 policy, CycleDetectingLockFactory.createNodes(enumClass));
98 }
99
100 public void testDeadlock_twoLocks() {
101
102 lockA.lock();
103 lockB.lock();
104 lockA.unlock();
105 lockB.unlock();
106
107
108 PotentialDeadlockException firstException = null;
109 lockB.lock();
110 try {
111 lockA.lock();
112 fail("Expected PotentialDeadlockException");
113 } catch (PotentialDeadlockException expected) {
114 checkMessage(expected, "LockB -> LockA", "LockA -> LockB");
115 firstException = expected;
116 }
117
118
119 try {
120 lockA.lock();
121 fail("Expected PotentialDeadlockException");
122 } catch (PotentialDeadlockException expected) {
123 checkMessage(expected, "LockB -> LockA", "LockA -> LockB");
124
125 assertSame(firstException.getCause(), expected.getCause());
126 }
127
128
129 lockB.unlock();
130 lockA.lock();
131 }
132
133
134 public void testDeadlock_threeLocks() {
135
136 lockA.lock();
137 lockB.lock();
138 lockB.unlock();
139 lockA.unlock();
140
141
142 lockB.lock();
143 lockC.lock();
144 lockB.unlock();
145
146
147 try {
148 lockA.lock();
149 fail("Expected PotentialDeadlockException");
150 } catch (PotentialDeadlockException expected) {
151 checkMessage(
152 expected, "LockC -> LockA", "LockB -> LockC", "LockA -> LockB");
153 }
154 }
155
156 public void testReentrancy_noDeadlock() {
157 lockA.lock();
158 lockB.lock();
159 lockA.lock();
160 }
161
162 public void testExplicitOrdering_noViolations() {
163 lock1.lock();
164 lock3.lock();
165 lock3.unlock();
166 lock2.lock();
167 lock3.lock();
168 }
169
170 public void testExplicitOrdering_violations() {
171 lock3.lock();
172 try {
173 lock2.lock();
174 fail("Expected PotentialDeadlockException");
175 } catch (PotentialDeadlockException expected) {
176 checkMessage(expected, "MyOrder.THIRD -> MyOrder.SECOND");
177 }
178
179 try {
180 lock1.lock();
181 fail("Expected PotentialDeadlockException");
182 } catch (PotentialDeadlockException expected) {
183 checkMessage(expected, "MyOrder.THIRD -> MyOrder.FIRST");
184 }
185
186 lock3.unlock();
187 lock2.lock();
188
189 try {
190 lock1.lock();
191 fail("Expected PotentialDeadlockException");
192 } catch (PotentialDeadlockException expected) {
193 checkMessage(expected, "MyOrder.SECOND -> MyOrder.FIRST");
194 }
195 }
196
197 public void testDifferentOrderings_noViolations() {
198 lock3.lock();
199 lock01.lock();
200 }
201
202 public void testExplicitOrderings_generalCycleDetection() {
203 lock3.lock();
204 lock01.lock();
205
206 lock3.unlock();
207 try {
208 lock3.lock();
209 fail("Expected PotentialDeadlockException");
210 } catch (PotentialDeadlockException expected) {
211 checkMessage(
212 expected,
213 "OtherOrder.FIRST -> MyOrder.THIRD",
214 "MyOrder.THIRD -> OtherOrder.FIRST");
215 }
216
217 lockA.lock();
218 lock01.unlock();
219 lockB.lock();
220 lockA.unlock();
221
222 try {
223 lock01.lock();
224 fail("Expected PotentialDeadlockException");
225 } catch (PotentialDeadlockException expected) {
226 checkMessage(
227 expected,
228 "LockB -> OtherOrder.FIRST",
229 "LockA -> LockB",
230 "OtherOrder.FIRST -> LockA");
231 }
232 }
233
234 public void testExplicitOrdering_cycleWithUnorderedLock() {
235 Lock myLock = CycleDetectingLockFactory.newInstance(Policies.THROW)
236 .newReentrantLock("MyLock");
237 lock03.lock();
238 myLock.lock();
239 lock03.unlock();
240
241 try {
242 lock01.lock();
243 fail("Expected PotentialDeadlockException");
244 } catch (PotentialDeadlockException expected) {
245 checkMessage(
246 expected,
247 "MyLock -> OtherOrder.FIRST",
248 "OtherOrder.THIRD -> MyLock",
249 "OtherOrder.FIRST -> OtherOrder.THIRD");
250 }
251 }
252
253 public void testExplicitOrdering_reentrantAcquisition() {
254 CycleDetectingLockFactory.WithExplicitOrdering<OtherOrder> factory =
255 newInstanceWithExplicitOrdering(OtherOrder.class, Policies.THROW);
256 Lock lockA = factory.newReentrantReadWriteLock(OtherOrder.FIRST).readLock();
257 Lock lockB = factory.newReentrantLock(OtherOrder.SECOND);
258
259 lockA.lock();
260 lockA.lock();
261 lockB.lock();
262 lockB.lock();
263 lockA.unlock();
264 lockA.unlock();
265 lockB.unlock();
266 lockB.unlock();
267 }
268
269 public void testExplicitOrdering_acquiringMultipleLocksWithSameRank() {
270 CycleDetectingLockFactory.WithExplicitOrdering<OtherOrder> factory =
271 newInstanceWithExplicitOrdering(OtherOrder.class, Policies.THROW);
272 Lock lockA = factory.newReentrantLock(OtherOrder.FIRST);
273 Lock lockB = factory.newReentrantReadWriteLock(OtherOrder.FIRST).readLock();
274
275 lockA.lock();
276 try {
277 lockB.lock();
278 fail("Expected IllegalStateException");
279 } catch (IllegalStateException expected) {
280 }
281
282 lockA.unlock();
283 lockB.lock();
284 }
285
286 public void testReadLock_deadlock() {
287 readLockA.lock();
288 lockB.lock();
289 lockB.unlock();
290 readLockA.unlock();
291
292 lockB.lock();
293 try {
294 readLockA.lock();
295 fail("Expected PotentialDeadlockException");
296 } catch (PotentialDeadlockException expected) {
297 checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB");
298 }
299 }
300
301 public void testReadLock_transitive() {
302 readLockA.lock();
303 lockB.lock();
304 lockB.unlock();
305 readLockA.unlock();
306
307
308 lockB.lock();
309 readLockC.lock();
310 lockB.unlock();
311 readLockC.unlock();
312
313
314 readLockC.lock();
315 try {
316 readLockA.lock();
317 fail("Expected PotentialDeadlockException");
318 } catch (PotentialDeadlockException expected) {
319 checkMessage(
320 expected,
321 "ReadWriteC -> ReadWriteA",
322 "LockB -> ReadWriteC",
323 "ReadWriteA -> LockB");
324 }
325 }
326
327 public void testWriteLock_threeLockDeadLock() {
328
329 writeLockA.lock();
330 writeLockB.lock();
331 writeLockB.unlock();
332 writeLockA.unlock();
333
334
335 writeLockB.lock();
336 writeLockC.lock();
337 writeLockB.unlock();
338
339
340 try {
341 writeLockA.lock();
342 fail("Expected PotentialDeadlockException");
343 } catch (PotentialDeadlockException expected) {
344 checkMessage(
345 expected,
346 "ReadWriteC -> ReadWriteA",
347 "ReadWriteB -> ReadWriteC",
348 "ReadWriteA -> ReadWriteB");
349 }
350 }
351
352 public void testWriteToReadLockDowngrading() {
353 writeLockA.lock();
354 readLockA.lock();
355 writeLockA.unlock();
356
357 lockB.lock();
358 readLockA.unlock();
359
360
361 try {
362 writeLockA.lock();
363 fail("Expected PotentialDeadlockException");
364 } catch (PotentialDeadlockException expected) {
365 checkMessage(
366 expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB");
367 }
368 }
369
370 public void testReadWriteLockDeadlock() {
371 writeLockA.lock();
372 lockB.lock();
373 writeLockA.unlock();
374 lockB.unlock();
375
376
377 lockB.lock();
378 try {
379 readLockA.lock();
380 fail("Expected PotentialDeadlockException");
381 } catch (PotentialDeadlockException expected) {
382 checkMessage(
383 expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB");
384 }
385 }
386
387 public void testReadWriteLockDeadlock_transitive() {
388 readLockA.lock();
389 lockB.lock();
390 readLockA.unlock();
391 lockB.unlock();
392
393
394 lockB.lock();
395 lockC.lock();
396 lockB.unlock();
397 lockC.unlock();
398
399
400 lockC.lock();
401 try {
402 writeLockA.lock();
403 fail("Expected PotentialDeadlockException");
404 } catch (PotentialDeadlockException expected) {
405 checkMessage(
406 expected,
407 "LockC -> ReadWriteA",
408 "LockB -> LockC",
409 "ReadWriteA -> LockB");
410 }
411 }
412
413 public void testReadWriteLockDeadlock_treatedEquivalently() {
414 readLockA.lock();
415 writeLockB.lock();
416 readLockA.unlock();
417 writeLockB.unlock();
418
419
420 readLockB.lock();
421 try {
422 writeLockA.lock();
423 fail("Expected PotentialDeadlockException");
424 } catch (PotentialDeadlockException expected) {
425 checkMessage(
426 expected, "ReadWriteB -> ReadWriteA", "ReadWriteA -> ReadWriteB");
427 }
428 }
429
430 public void testDifferentLockFactories() {
431 CycleDetectingLockFactory otherFactory =
432 CycleDetectingLockFactory.newInstance(Policies.WARN);
433 ReentrantLock lockD = otherFactory.newReentrantLock("LockD");
434
435
436 lockA.lock();
437 lockD.lock();
438 lockA.unlock();
439 lockD.unlock();
440
441
442 lockD.lock();
443 try {
444 lockA.lock();
445 fail("Expected PotentialDeadlockException");
446 } catch (PotentialDeadlockException expected) {
447 checkMessage(expected, "LockD -> LockA", "LockA -> LockD");
448 }
449 }
450
451 public void testDifferentLockFactories_policyExecution() {
452 CycleDetectingLockFactory otherFactory =
453 CycleDetectingLockFactory.newInstance(Policies.WARN);
454 ReentrantLock lockD = otherFactory.newReentrantLock("LockD");
455
456
457 lockD.lock();
458 lockA.lock();
459 lockA.unlock();
460 lockD.unlock();
461
462
463
464 lockA.lock();
465 lockD.lock();
466 }
467
468 public void testReentrantLock_tryLock() throws Exception {
469 LockingThread thread = new LockingThread(lockA);
470 thread.start();
471
472 thread.waitUntilHoldingLock();
473 assertFalse(lockA.tryLock());
474
475 thread.releaseLockAndFinish();
476 assertTrue(lockA.tryLock());
477 }
478
479 public void testReentrantWriteLock_tryLock() throws Exception {
480 LockingThread thread = new LockingThread(writeLockA);
481 thread.start();
482
483 thread.waitUntilHoldingLock();
484 assertFalse(writeLockA.tryLock());
485 assertFalse(readLockA.tryLock());
486
487 thread.releaseLockAndFinish();
488 assertTrue(writeLockA.tryLock());
489 assertTrue(readLockA.tryLock());
490 }
491
492 public void testReentrantReadLock_tryLock() throws Exception {
493 LockingThread thread = new LockingThread(readLockA);
494 thread.start();
495
496 thread.waitUntilHoldingLock();
497 assertFalse(writeLockA.tryLock());
498 assertTrue(readLockA.tryLock());
499 readLockA.unlock();
500
501 thread.releaseLockAndFinish();
502 assertTrue(writeLockA.tryLock());
503 assertTrue(readLockA.tryLock());
504 }
505
506 private static class LockingThread extends Thread {
507 final CountDownLatch locked = new CountDownLatch(1);
508 final CountDownLatch finishLatch = new CountDownLatch(1);
509 final Lock lock;
510
511 LockingThread(Lock lock) {
512 this.lock = lock;
513 }
514
515 @Override
516 public void run() {
517 lock.lock();
518 try {
519 locked.countDown();
520 finishLatch.await(1, TimeUnit.MINUTES);
521 } catch (InterruptedException e) {
522 fail(e.toString());
523 } finally {
524 lock.unlock();
525 }
526 }
527
528 void waitUntilHoldingLock() throws InterruptedException {
529 locked.await(1, TimeUnit.MINUTES);
530 }
531
532 void releaseLockAndFinish() throws InterruptedException {
533 finishLatch.countDown();
534 this.join(10000);
535 assertFalse(this.isAlive());
536 }
537 }
538
539 public void testReentrantReadWriteLock_implDoesNotExposeShadowedLocks() {
540 assertEquals(
541 "Unexpected number of public methods in ReentrantReadWriteLock. " +
542 "The correctness of CycleDetectingReentrantReadWriteLock depends on " +
543 "the fact that the shadowed ReadLock and WriteLock are never used or " +
544 "exposed by the superclass implementation. If the implementation has " +
545 "changed, the code must be re-inspected to ensure that the " +
546 "assumption is still valid.",
547 24, ReentrantReadWriteLock.class.getMethods().length);
548 }
549
550 private enum MyOrder {
551 FIRST, SECOND, THIRD;
552 }
553
554 private enum OtherOrder {
555 FIRST, SECOND, THIRD;
556 }
557
558
559
560
561
562 private void checkMessage(
563 IllegalStateException exception, String... expectedLockCycle) {
564 String regex = Joiner.on("\\b.*\\b").join(expectedLockCycle);
565 assertContainsRegex(regex, exception.getMessage());
566 }
567
568
569 private static void assertContainsRegex(String expectedRegex, String actual) {
570 Pattern pattern = Pattern.compile(expectedRegex);
571 Matcher matcher = pattern.matcher(actual);
572 if (!matcher.find()) {
573 String actualDesc = (actual == null) ? "null" : ('<' + actual + '>');
574 fail("expected to contain regex:<" + expectedRegex + "> but was:"
575 + actualDesc);
576 }
577 }
578 }