• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.util.concurrent;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static org.junit.Assert.assertThrows;
21 
22 import com.google.common.base.Joiner;
23 import com.google.common.util.concurrent.CycleDetectingLockFactory.Policies;
24 import com.google.common.util.concurrent.CycleDetectingLockFactory.Policy;
25 import com.google.common.util.concurrent.CycleDetectingLockFactory.PotentialDeadlockException;
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 junit.framework.TestCase;
32 
33 /**
34  * Unittests for {@link CycleDetectingLockFactory}.
35  *
36  * @author Darick Tong
37  */
38 public class CycleDetectingLockFactoryTest extends TestCase {
39 
40   private ReentrantLock lockA;
41   private ReentrantLock lockB;
42   private ReentrantLock lockC;
43   private ReentrantReadWriteLock.ReadLock readLockA;
44   private ReentrantReadWriteLock.ReadLock readLockB;
45   private ReentrantReadWriteLock.ReadLock readLockC;
46   private ReentrantReadWriteLock.WriteLock writeLockA;
47   private ReentrantReadWriteLock.WriteLock writeLockB;
48   private ReentrantReadWriteLock.WriteLock writeLockC;
49   private ReentrantLock lock1;
50   private ReentrantLock lock2;
51   private ReentrantLock lock3;
52   private ReentrantLock lock01;
53   private ReentrantLock lock02;
54   private ReentrantLock lock03;
55 
56   @Override
setUp()57   protected void setUp() throws Exception {
58     super.setUp();
59     CycleDetectingLockFactory factory = CycleDetectingLockFactory.newInstance(Policies.THROW);
60     lockA = factory.newReentrantLock("LockA");
61     lockB = factory.newReentrantLock("LockB");
62     lockC = factory.newReentrantLock("LockC");
63     ReentrantReadWriteLock readWriteLockA = factory.newReentrantReadWriteLock("ReadWriteA");
64     ReentrantReadWriteLock readWriteLockB = factory.newReentrantReadWriteLock("ReadWriteB");
65     ReentrantReadWriteLock readWriteLockC = factory.newReentrantReadWriteLock("ReadWriteC");
66     readLockA = readWriteLockA.readLock();
67     readLockB = readWriteLockB.readLock();
68     readLockC = readWriteLockC.readLock();
69     writeLockA = readWriteLockA.writeLock();
70     writeLockB = readWriteLockB.writeLock();
71     writeLockC = readWriteLockC.writeLock();
72 
73     CycleDetectingLockFactory.WithExplicitOrdering<MyOrder> factory2 =
74         newInstanceWithExplicitOrdering(MyOrder.class, Policies.THROW);
75     lock1 = factory2.newReentrantLock(MyOrder.FIRST);
76     lock2 = factory2.newReentrantLock(MyOrder.SECOND);
77     lock3 = factory2.newReentrantLock(MyOrder.THIRD);
78 
79     CycleDetectingLockFactory.WithExplicitOrdering<OtherOrder> factory3 =
80         newInstanceWithExplicitOrdering(OtherOrder.class, Policies.THROW);
81     lock01 = factory3.newReentrantLock(OtherOrder.FIRST);
82     lock02 = factory3.newReentrantLock(OtherOrder.SECOND);
83     lock03 = factory3.newReentrantLock(OtherOrder.THIRD);
84   }
85 
86   // In the unittest, create each ordered factory with its own set of lock
87   // graph nodes (as opposed to using the static per-Enum map) to avoid
88   // conflicts across different test runs.
89   private <E extends Enum<E>>
newInstanceWithExplicitOrdering( Class<E> enumClass, Policy policy)90       CycleDetectingLockFactory.WithExplicitOrdering<E> newInstanceWithExplicitOrdering(
91           Class<E> enumClass, Policy policy) {
92     return new CycleDetectingLockFactory.WithExplicitOrdering<E>(
93         policy, CycleDetectingLockFactory.createNodes(enumClass));
94   }
95 
testDeadlock_twoLocks()96   public void testDeadlock_twoLocks() {
97     // Establish an acquisition order of lockA -> lockB.
98     lockA.lock();
99     lockB.lock();
100     lockA.unlock();
101     lockB.unlock();
102 
103     // The opposite order should fail (Policies.THROW).
104     PotentialDeadlockException firstException = null;
105     lockB.lock();
106     PotentialDeadlockException expected =
107         assertThrows(PotentialDeadlockException.class, () -> lockA.lock());
108     checkMessage(expected, "LockB -> LockA", "LockA -> LockB");
109     firstException = expected;
110     // Second time should also fail, with a cached causal chain.
111     expected = assertThrows(PotentialDeadlockException.class, () -> lockA.lock());
112     checkMessage(expected, "LockB -> LockA", "LockA -> LockB");
113     // The causal chain should be cached.
114     assertSame(firstException.getCause(), expected.getCause());
115     // lockA should work after lockB is released.
116     lockB.unlock();
117     lockA.lock();
118   }
119 
120   // Tests transitive deadlock detection.
testDeadlock_threeLocks()121   public void testDeadlock_threeLocks() {
122     // Establish an ordering from lockA -> lockB.
123     lockA.lock();
124     lockB.lock();
125     lockB.unlock();
126     lockA.unlock();
127 
128     // Establish an ordering from lockB -> lockC.
129     lockB.lock();
130     lockC.lock();
131     lockB.unlock();
132 
133     // lockC -> lockA should fail.
134     PotentialDeadlockException expected =
135         assertThrows(PotentialDeadlockException.class, () -> lockA.lock());
136     checkMessage(expected, "LockC -> LockA", "LockB -> LockC", "LockA -> LockB");
137   }
138 
testReentrancy_noDeadlock()139   public void testReentrancy_noDeadlock() {
140     lockA.lock();
141     lockB.lock();
142     lockA.lock(); // Should not assert on lockB -> reentrant(lockA)
143   }
144 
testExplicitOrdering_noViolations()145   public void testExplicitOrdering_noViolations() {
146     lock1.lock();
147     lock3.lock();
148     lock3.unlock();
149     lock2.lock();
150     lock3.lock();
151   }
152 
testExplicitOrdering_violations()153   public void testExplicitOrdering_violations() {
154     lock3.lock();
155     PotentialDeadlockException expected =
156         assertThrows(PotentialDeadlockException.class, () -> lock2.lock());
157     checkMessage(expected, "MyOrder.THIRD -> MyOrder.SECOND");
158 
159     expected = assertThrows(PotentialDeadlockException.class, () -> lock1.lock());
160     checkMessage(expected, "MyOrder.THIRD -> MyOrder.FIRST");
161 
162     lock3.unlock();
163     lock2.lock();
164 
165     expected = assertThrows(PotentialDeadlockException.class, () -> lock1.lock());
166     checkMessage(expected, "MyOrder.SECOND -> MyOrder.FIRST");
167   }
168 
testDifferentOrderings_noViolations()169   public void testDifferentOrderings_noViolations() {
170     lock3.lock(); // MyOrder, ordinal() == 3
171     lock01.lock(); // OtherOrder, ordinal() == 1
172   }
173 
testExplicitOrderings_generalCycleDetection()174   public void testExplicitOrderings_generalCycleDetection() {
175     lock3.lock(); // MyOrder, ordinal() == 3
176     lock01.lock(); // OtherOrder, ordinal() == 1
177 
178     lock3.unlock();
179     PotentialDeadlockException expected =
180         assertThrows(PotentialDeadlockException.class, () -> lock3.lock());
181     checkMessage(
182         expected, "OtherOrder.FIRST -> MyOrder.THIRD", "MyOrder.THIRD -> OtherOrder.FIRST");
183     lockA.lock();
184     lock01.unlock();
185     lockB.lock();
186     lockA.unlock();
187 
188     expected = assertThrows(PotentialDeadlockException.class, () -> lock01.lock());
189     checkMessage(
190         expected, "LockB -> OtherOrder.FIRST", "LockA -> LockB", "OtherOrder.FIRST -> LockA");
191   }
192 
testExplicitOrdering_cycleWithUnorderedLock()193   public void testExplicitOrdering_cycleWithUnorderedLock() {
194     Lock myLock = CycleDetectingLockFactory.newInstance(Policies.THROW).newReentrantLock("MyLock");
195     lock03.lock();
196     myLock.lock();
197     lock03.unlock();
198 
199     PotentialDeadlockException expected =
200         assertThrows(PotentialDeadlockException.class, () -> lock01.lock());
201     checkMessage(
202         expected,
203         "MyLock -> OtherOrder.FIRST",
204         "OtherOrder.THIRD -> MyLock",
205         "OtherOrder.FIRST -> OtherOrder.THIRD");
206   }
207 
testExplicitOrdering_reentrantAcquisition()208   public void testExplicitOrdering_reentrantAcquisition() {
209     CycleDetectingLockFactory.WithExplicitOrdering<OtherOrder> factory =
210         newInstanceWithExplicitOrdering(OtherOrder.class, Policies.THROW);
211     Lock lockA = factory.newReentrantReadWriteLock(OtherOrder.FIRST).readLock();
212     Lock lockB = factory.newReentrantLock(OtherOrder.SECOND);
213 
214     lockA.lock();
215     lockA.lock();
216     lockB.lock();
217     lockB.lock();
218     lockA.unlock();
219     lockA.unlock();
220     lockB.unlock();
221     lockB.unlock();
222   }
223 
testExplicitOrdering_acquiringMultipleLocksWithSameRank()224   public void testExplicitOrdering_acquiringMultipleLocksWithSameRank() {
225     CycleDetectingLockFactory.WithExplicitOrdering<OtherOrder> factory =
226         newInstanceWithExplicitOrdering(OtherOrder.class, Policies.THROW);
227     Lock lockA = factory.newReentrantLock(OtherOrder.FIRST);
228     Lock lockB = factory.newReentrantReadWriteLock(OtherOrder.FIRST).readLock();
229 
230     lockA.lock();
231     assertThrows(IllegalStateException.class, () -> lockB.lock());
232 
233     lockA.unlock();
234     lockB.lock();
235   }
236 
testReadLock_deadlock()237   public void testReadLock_deadlock() {
238     readLockA.lock(); // Establish an ordering from readLockA -> lockB.
239     lockB.lock();
240     lockB.unlock();
241     readLockA.unlock();
242 
243     lockB.lock();
244     PotentialDeadlockException expected =
245         assertThrows(PotentialDeadlockException.class, () -> readLockA.lock());
246     checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB");
247   }
248 
testReadLock_transitive()249   public void testReadLock_transitive() {
250     readLockA.lock(); // Establish an ordering from readLockA -> lockB.
251     lockB.lock();
252     lockB.unlock();
253     readLockA.unlock();
254 
255     // Establish an ordering from lockB -> readLockC.
256     lockB.lock();
257     readLockC.lock();
258     lockB.unlock();
259     readLockC.unlock();
260 
261     // readLockC -> readLockA
262     readLockC.lock();
263     PotentialDeadlockException expected =
264         assertThrows(PotentialDeadlockException.class, () -> readLockA.lock());
265     checkMessage(
266         expected, "ReadWriteC -> ReadWriteA", "LockB -> ReadWriteC", "ReadWriteA -> LockB");
267   }
268 
testWriteLock_threeLockDeadLock()269   public void testWriteLock_threeLockDeadLock() {
270     // Establish an ordering from writeLockA -> writeLockB.
271     writeLockA.lock();
272     writeLockB.lock();
273     writeLockB.unlock();
274     writeLockA.unlock();
275 
276     // Establish an ordering from writeLockB -> writeLockC.
277     writeLockB.lock();
278     writeLockC.lock();
279     writeLockB.unlock();
280 
281     // writeLockC -> writeLockA should fail.
282     PotentialDeadlockException expected =
283         assertThrows(PotentialDeadlockException.class, () -> writeLockA.lock());
284     checkMessage(
285         expected,
286         "ReadWriteC -> ReadWriteA",
287         "ReadWriteB -> ReadWriteC",
288         "ReadWriteA -> ReadWriteB");
289   }
290 
testWriteToReadLockDowngrading()291   public void testWriteToReadLockDowngrading() {
292     writeLockA.lock(); // writeLockA downgrades to readLockA
293     readLockA.lock();
294     writeLockA.unlock();
295 
296     lockB.lock(); // readLockA -> lockB
297     readLockA.unlock();
298 
299     // lockB -> writeLockA should fail
300     PotentialDeadlockException expected =
301         assertThrows(PotentialDeadlockException.class, () -> writeLockA.lock());
302     checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB");
303   }
304 
testReadWriteLockDeadlock()305   public void testReadWriteLockDeadlock() {
306     writeLockA.lock(); // Establish an ordering from writeLockA -> lockB
307     lockB.lock();
308     writeLockA.unlock();
309     lockB.unlock();
310 
311     // lockB -> readLockA should fail.
312     lockB.lock();
313     PotentialDeadlockException expected =
314         assertThrows(PotentialDeadlockException.class, () -> readLockA.lock());
315     checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB");
316   }
317 
testReadWriteLockDeadlock_transitive()318   public void testReadWriteLockDeadlock_transitive() {
319     readLockA.lock(); // Establish an ordering from readLockA -> lockB
320     lockB.lock();
321     readLockA.unlock();
322     lockB.unlock();
323 
324     // Establish an ordering from lockB -> lockC
325     lockB.lock();
326     lockC.lock();
327     lockB.unlock();
328     lockC.unlock();
329 
330     // lockC -> writeLockA should fail.
331     lockC.lock();
332     PotentialDeadlockException expected =
333         assertThrows(PotentialDeadlockException.class, () -> writeLockA.lock());
334     checkMessage(expected, "LockC -> ReadWriteA", "LockB -> LockC", "ReadWriteA -> LockB");
335   }
336 
testReadWriteLockDeadlock_treatedEquivalently()337   public void testReadWriteLockDeadlock_treatedEquivalently() {
338     readLockA.lock(); // readLockA -> writeLockB
339     writeLockB.lock();
340     readLockA.unlock();
341     writeLockB.unlock();
342 
343     // readLockB -> writeLockA should fail.
344     readLockB.lock();
345     PotentialDeadlockException expected =
346         assertThrows(PotentialDeadlockException.class, () -> writeLockA.lock());
347     checkMessage(expected, "ReadWriteB -> ReadWriteA", "ReadWriteA -> ReadWriteB");
348   }
349 
testDifferentLockFactories()350   public void testDifferentLockFactories() {
351     CycleDetectingLockFactory otherFactory = CycleDetectingLockFactory.newInstance(Policies.WARN);
352     ReentrantLock lockD = otherFactory.newReentrantLock("LockD");
353 
354     // lockA -> lockD
355     lockA.lock();
356     lockD.lock();
357     lockA.unlock();
358     lockD.unlock();
359 
360     // lockD -> lockA should fail even though lockD is from a different factory.
361     lockD.lock();
362     PotentialDeadlockException expected =
363         assertThrows(PotentialDeadlockException.class, () -> lockA.lock());
364     checkMessage(expected, "LockD -> LockA", "LockA -> LockD");
365   }
366 
testDifferentLockFactories_policyExecution()367   public void testDifferentLockFactories_policyExecution() {
368     CycleDetectingLockFactory otherFactory = CycleDetectingLockFactory.newInstance(Policies.WARN);
369     ReentrantLock lockD = otherFactory.newReentrantLock("LockD");
370 
371     // lockD -> lockA
372     lockD.lock();
373     lockA.lock();
374     lockA.unlock();
375     lockD.unlock();
376 
377     // lockA -> lockD should warn but otherwise succeed because lockD was
378     // created by a factory with the WARN policy.
379     lockA.lock();
380     lockD.lock();
381   }
382 
testReentrantLock_tryLock()383   public void testReentrantLock_tryLock() throws Exception {
384     LockingThread thread = new LockingThread(lockA);
385     thread.start();
386 
387     thread.waitUntilHoldingLock();
388     assertFalse(lockA.tryLock());
389 
390     thread.releaseLockAndFinish();
391     assertTrue(lockA.tryLock());
392   }
393 
testReentrantWriteLock_tryLock()394   public void testReentrantWriteLock_tryLock() throws Exception {
395     LockingThread thread = new LockingThread(writeLockA);
396     thread.start();
397 
398     thread.waitUntilHoldingLock();
399     assertFalse(writeLockA.tryLock());
400     assertFalse(readLockA.tryLock());
401 
402     thread.releaseLockAndFinish();
403     assertTrue(writeLockA.tryLock());
404     assertTrue(readLockA.tryLock());
405   }
406 
testReentrantReadLock_tryLock()407   public void testReentrantReadLock_tryLock() throws Exception {
408     LockingThread thread = new LockingThread(readLockA);
409     thread.start();
410 
411     thread.waitUntilHoldingLock();
412     assertFalse(writeLockA.tryLock());
413     assertTrue(readLockA.tryLock());
414     readLockA.unlock();
415 
416     thread.releaseLockAndFinish();
417     assertTrue(writeLockA.tryLock());
418     assertTrue(readLockA.tryLock());
419   }
420 
421   private static class LockingThread extends Thread {
422     final CountDownLatch locked = new CountDownLatch(1);
423     final CountDownLatch finishLatch = new CountDownLatch(1);
424     final Lock lock;
425 
LockingThread(Lock lock)426     LockingThread(Lock lock) {
427       this.lock = lock;
428     }
429 
430     @Override
run()431     public void run() {
432       lock.lock();
433       try {
434         locked.countDown();
435         finishLatch.await(1, TimeUnit.MINUTES);
436       } catch (InterruptedException e) {
437         fail(e.toString());
438       } finally {
439         lock.unlock();
440       }
441     }
442 
waitUntilHoldingLock()443     void waitUntilHoldingLock() throws InterruptedException {
444       locked.await(1, TimeUnit.MINUTES);
445     }
446 
releaseLockAndFinish()447     void releaseLockAndFinish() throws InterruptedException {
448       finishLatch.countDown();
449       this.join(10000);
450       assertFalse(this.isAlive());
451     }
452   }
453 
testReentrantReadWriteLock_implDoesNotExposeShadowedLocks()454   public void testReentrantReadWriteLock_implDoesNotExposeShadowedLocks() {
455     assertEquals(
456         "Unexpected number of public methods in ReentrantReadWriteLock. "
457             + "The correctness of CycleDetectingReentrantReadWriteLock depends on "
458             + "the fact that the shadowed ReadLock and WriteLock are never used or "
459             + "exposed by the superclass implementation. If the implementation has "
460             + "changed, the code must be re-inspected to ensure that the "
461             + "assumption is still valid.",
462         24,
463         ReentrantReadWriteLock.class.getMethods().length);
464   }
465 
466   private enum MyOrder {
467     FIRST,
468     SECOND,
469     THIRD;
470   }
471 
472   private enum OtherOrder {
473     FIRST,
474     SECOND,
475     THIRD;
476   }
477 
478   // Given a sequence of lock acquisition descriptions
479   // (e.g. "LockA -> LockB", "LockB -> LockC", ...)
480   // Checks that the exception.getMessage() matches a regex of the form:
481   // "LockA -> LockB \b.*\b LockB -> LockC \b.*\b LockC -> LockA"
checkMessage(IllegalStateException exception, String... expectedLockCycle)482   private void checkMessage(IllegalStateException exception, String... expectedLockCycle) {
483     String regex = Joiner.on("\\b.*\\b").join(expectedLockCycle);
484     assertThat(exception).hasMessageThat().containsMatch(regex);
485   }
486 }
487