View Javadoc
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.base.Preconditions.checkNotNull;
20  
21  import com.google.common.annotations.Beta;
22  import com.google.common.annotations.VisibleForTesting;
23  import com.google.common.base.MoreObjects;
24  import com.google.common.base.Preconditions;
25  import com.google.common.collect.ImmutableSet;
26  import com.google.common.collect.Lists;
27  import com.google.common.collect.MapMaker;
28  import com.google.common.collect.Maps;
29  import com.google.common.collect.Sets;
30  
31  import java.util.ArrayList;
32  import java.util.Arrays;
33  import java.util.Collections;
34  import java.util.EnumMap;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Set;
38  import java.util.concurrent.ConcurrentMap;
39  import java.util.concurrent.TimeUnit;
40  import java.util.concurrent.locks.ReentrantLock;
41  import java.util.concurrent.locks.ReentrantReadWriteLock;
42  import java.util.logging.Level;
43  import java.util.logging.Logger;
44  
45  import javax.annotation.Nullable;
46  import javax.annotation.concurrent.ThreadSafe;
47  
48  /**
49   * The {@code CycleDetectingLockFactory} creates {@link ReentrantLock} instances and
50   * {@link ReentrantReadWriteLock} instances that detect potential deadlock by checking
51   * for cycles in lock acquisition order.
52   * <p>
53   * Potential deadlocks detected when calling the {@code lock()},
54   * {@code lockInterruptibly()}, or {@code tryLock()} methods will result in the
55   * execution of the {@link Policy} specified when creating the factory. The
56   * currently available policies are:
57   * <ul>
58   * <li>DISABLED
59   * <li>WARN
60   * <li>THROW
61   * </ul>
62   * <p>The locks created by a factory instance will detect lock acquisition cycles
63   * with locks created by other {@code CycleDetectingLockFactory} instances
64   * (except those with {@code Policy.DISABLED}). A lock's behavior when a cycle
65   * is detected, however, is defined by the {@code Policy} of the factory that
66   * created it. This allows detection of cycles across components while
67   * delegating control over lock behavior to individual components.
68   * <p>
69   * Applications are encouraged to use a {@code CycleDetectingLockFactory} to
70   * create any locks for which external/unmanaged code is executed while the lock
71   * is held. (See caveats under <strong>Performance</strong>).
72   * <p>
73   * <strong>Cycle Detection</strong>
74   * <p>
75   * Deadlocks can arise when locks are acquired in an order that forms a cycle.
76   * In a simple example involving two locks and two threads, deadlock occurs
77   * when one thread acquires Lock A, and then Lock B, while another thread
78   * acquires Lock B, and then Lock A:
79   * <pre>
80   * Thread1: acquire(LockA) --X acquire(LockB)
81   * Thread2: acquire(LockB) --X acquire(LockA)
82   * </pre>
83   * <p>Neither thread will progress because each is waiting for the other. In more
84   * complex applications, cycles can arise from interactions among more than 2
85   * locks:
86   * <pre>
87   * Thread1: acquire(LockA) --X acquire(LockB)
88   * Thread2: acquire(LockB) --X acquire(LockC)
89   * ...
90   * ThreadN: acquire(LockN) --X acquire(LockA)
91   * </pre>
92   * <p>The implementation detects cycles by constructing a directed graph in which
93   * each lock represents a node and each edge represents an acquisition ordering
94   * between two locks.
95   * <ul>
96   * <li>Each lock adds (and removes) itself to/from a ThreadLocal Set of acquired
97   *   locks when the Thread acquires its first hold (and releases its last
98   *   remaining hold).
99   * <li>Before the lock is acquired, the lock is checked against the current set
100  *   of acquired locks---to each of the acquired locks, an edge from the
101  *   soon-to-be-acquired lock is either verified or created.
102  * <li>If a new edge needs to be created, the outgoing edges of the acquired
103  *   locks are traversed to check for a cycle that reaches the lock to be
104  *   acquired. If no cycle is detected, a new "safe" edge is created.
105  * <li>If a cycle is detected, an "unsafe" (cyclic) edge is created to represent
106  *   a potential deadlock situation, and the appropriate Policy is executed.
107  * </ul>
108  * <p>Note that detection of potential deadlock does not necessarily indicate that
109  * deadlock will happen, as it is possible that higher level application logic
110  * prevents the cyclic lock acquisition from occurring. One example of a false
111  * positive is:
112  * <pre>
113  * LockA -&gt; LockB -&gt; LockC
114  * LockA -&gt; LockC -&gt; LockB
115  * </pre>
116  *
117  * <strong>ReadWriteLocks</strong>
118  * <p>
119  * While {@code ReadWriteLock} instances have different properties and can form cycles
120  * without potential deadlock, this class treats {@code ReadWriteLock} instances as
121  * equivalent to traditional exclusive locks. Although this increases the false
122  * positives that the locks detect (i.e. cycles that will not actually result in
123  * deadlock), it simplifies the algorithm and implementation considerably. The
124  * assumption is that a user of this factory wishes to eliminate any cyclic
125  * acquisition ordering.
126  * <p>
127  * <strong>Explicit Lock Acquisition Ordering</strong>
128  * <p>
129  * The {@link CycleDetectingLockFactory.WithExplicitOrdering} class can be used
130  * to enforce an application-specific ordering in addition to performing general
131  * cycle detection.
132  * <p>
133  * <strong>Garbage Collection</strong>
134  * <p>
135  * In order to allow proper garbage collection of unused locks, the edges of
136  * the lock graph are weak references.
137  * <p>
138  * <strong>Performance</strong>
139  * <p>
140  * The extra bookkeeping done by cycle detecting locks comes at some cost to
141  * performance. Benchmarks (as of December 2011) show that:
142  *
143  * <ul>
144  * <li>for an unnested {@code lock()} and {@code unlock()}, a cycle detecting
145  *   lock takes 38ns as opposed to the 24ns taken by a plain lock.
146  * <li>for nested locking, the cost increases with the depth of the nesting:
147  *   <ul>
148  *   <li> 2 levels: average of 64ns per lock()/unlock()
149  *   <li> 3 levels: average of 77ns per lock()/unlock()
150  *   <li> 4 levels: average of 99ns per lock()/unlock()
151  *   <li> 5 levels: average of 103ns per lock()/unlock()
152  *   <li>10 levels: average of 184ns per lock()/unlock()
153  *   <li>20 levels: average of 393ns per lock()/unlock()
154  *   </ul>
155  * </ul>
156  *
157  * <p>As such, the CycleDetectingLockFactory may not be suitable for
158  * performance-critical applications which involve tightly-looped or
159  * deeply-nested locking algorithms.
160  *
161  * @author Darick Tong
162  * @since 13.0
163  */
164 @Beta
165 @ThreadSafe
166 public class CycleDetectingLockFactory {
167 
168   /**
169    * Encapsulates the action to be taken when a potential deadlock is
170    * encountered. Clients can use one of the predefined {@link Policies} or
171    * specify a custom implementation. Implementations must be thread-safe.
172    *
173    * @since 13.0
174    */
175   @Beta
176   @ThreadSafe
177   public interface Policy {
178 
179     /**
180      * Called when a potential deadlock is encountered. Implementations can
181      * throw the given {@code exception} and/or execute other desired logic.
182      * <p>
183      * Note that the method will be called even upon an invocation of
184      * {@code tryLock()}. Although {@code tryLock()} technically recovers from
185      * deadlock by eventually timing out, this behavior is chosen based on the
186      * assumption that it is the application's wish to prohibit any cyclical
187      * lock acquisitions.
188      */
189     void handlePotentialDeadlock(PotentialDeadlockException exception);
190   }
191 
192   /**
193    * Pre-defined {@link Policy} implementations.
194    *
195    * @since 13.0
196    */
197   @Beta
198   public enum Policies implements Policy {
199     /**
200      * When potential deadlock is detected, this policy results in the throwing
201      * of the {@code PotentialDeadlockException} indicating the potential
202      * deadlock, which includes stack traces illustrating the cycle in lock
203      * acquisition order.
204      */
205     THROW {
206       @Override
207       public void handlePotentialDeadlock(PotentialDeadlockException e) {
208         throw e;
209       }
210     },
211 
212     /**
213      * When potential deadlock is detected, this policy results in the logging
214      * of a {@link Level#SEVERE} message indicating the potential deadlock,
215      * which includes stack traces illustrating the cycle in lock acquisition
216      * order.
217      */
218     WARN {
219       @Override
220       public void handlePotentialDeadlock(PotentialDeadlockException e) {
221         logger.log(Level.SEVERE, "Detected potential deadlock", e);
222       }
223     },
224 
225     /**
226      * Disables cycle detection. This option causes the factory to return
227      * unmodified lock implementations provided by the JDK, and is provided to
228      * allow applications to easily parameterize when cycle detection is
229      * enabled.
230      * <p>
231      * Note that locks created by a factory with this policy will <em>not</em>
232      * participate the cycle detection performed by locks created by other
233      * factories.
234      */
235     DISABLED {
236       @Override
237       public void handlePotentialDeadlock(PotentialDeadlockException e) {
238       }
239     };
240   }
241 
242   /**
243    * Creates a new factory with the specified policy.
244    */
245   public static CycleDetectingLockFactory newInstance(Policy policy) {
246     return new CycleDetectingLockFactory(policy);
247   }
248 
249   /**
250    * Equivalent to {@code newReentrantLock(lockName, false)}.
251    */
252   public ReentrantLock newReentrantLock(String lockName) {
253     return newReentrantLock(lockName, false);
254   }
255 
256   /**
257    * Creates a {@link ReentrantLock} with the given fairness policy. The
258    * {@code lockName} is used in the warning or exception output to help
259    * identify the locks involved in the detected deadlock.
260    */
261   public ReentrantLock newReentrantLock(String lockName, boolean fair) {
262     return policy == Policies.DISABLED ? new ReentrantLock(fair)
263         : new CycleDetectingReentrantLock(
264             new LockGraphNode(lockName), fair);
265   }
266 
267   /**
268    * Equivalent to {@code newReentrantReadWriteLock(lockName, false)}.
269    */
270   public ReentrantReadWriteLock newReentrantReadWriteLock(String lockName) {
271     return newReentrantReadWriteLock(lockName, false);
272   }
273 
274   /**
275    * Creates a {@link ReentrantReadWriteLock} with the given fairness policy.
276    * The {@code lockName} is used in the warning or exception output to help
277    * identify the locks involved in the detected deadlock.
278    */
279   public ReentrantReadWriteLock newReentrantReadWriteLock(
280       String lockName, boolean fair) {
281     return policy == Policies.DISABLED ? new ReentrantReadWriteLock(fair)
282         : new CycleDetectingReentrantReadWriteLock(
283             new LockGraphNode(lockName), fair);
284   }
285 
286   // A static mapping from an Enum type to its set of LockGraphNodes.
287   private static final ConcurrentMap<Class<? extends Enum>,
288       Map<? extends Enum, LockGraphNode>> lockGraphNodesPerType =
289           new MapMaker().weakKeys().makeMap();
290 
291   /**
292    * Creates a {@code CycleDetectingLockFactory.WithExplicitOrdering<E>}.
293    */
294   public static <E extends Enum<E>> WithExplicitOrdering<E>
295       newInstanceWithExplicitOrdering(Class<E> enumClass, Policy policy) {
296     // createNodes maps each enumClass to a Map with the corresponding enum key
297     // type.
298     checkNotNull(enumClass);
299     checkNotNull(policy);
300     @SuppressWarnings("unchecked")
301     Map<E, LockGraphNode> lockGraphNodes =
302         (Map<E, LockGraphNode>) getOrCreateNodes(enumClass);
303     return new WithExplicitOrdering<E>(policy, lockGraphNodes);
304   }
305 
306   private static Map<? extends Enum, LockGraphNode> getOrCreateNodes(
307       Class<? extends Enum> clazz) {
308     Map<? extends Enum, LockGraphNode> existing =
309         lockGraphNodesPerType.get(clazz);
310     if (existing != null) {
311       return existing;
312     }
313     Map<? extends Enum, LockGraphNode> created = createNodes(clazz);
314     existing = lockGraphNodesPerType.putIfAbsent(clazz, created);
315     return MoreObjects.firstNonNull(existing, created);
316   }
317 
318   /**
319    * For a given Enum type, creates an immutable map from each of the Enum's
320    * values to a corresponding LockGraphNode, with the
321    * {@code allowedPriorLocks} and {@code disallowedPriorLocks} prepopulated
322    * with nodes according to the natural ordering of the associated Enum values.
323    */
324   @VisibleForTesting
325   static <E extends Enum<E>> Map<E, LockGraphNode> createNodes(Class<E> clazz) {
326     EnumMap<E, LockGraphNode> map = Maps.newEnumMap(clazz);
327     E[] keys = clazz.getEnumConstants();
328     final int numKeys = keys.length;
329     ArrayList<LockGraphNode> nodes =
330         Lists.newArrayListWithCapacity(numKeys);
331     // Create a LockGraphNode for each enum value.
332     for (E key : keys) {
333       LockGraphNode node = new LockGraphNode(getLockName(key));
334       nodes.add(node);
335       map.put(key, node);
336     }
337     // Pre-populate all allowedPriorLocks with nodes of smaller ordinal.
338     for (int i = 1; i < numKeys; i++) {
339       nodes.get(i).checkAcquiredLocks(Policies.THROW, nodes.subList(0, i));
340     }
341     // Pre-populate all disallowedPriorLocks with nodes of larger ordinal.
342     for (int i = 0; i < numKeys - 1; i++) {
343       nodes.get(i).checkAcquiredLocks(
344           Policies.DISABLED, nodes.subList(i + 1, numKeys));
345     }
346     return Collections.unmodifiableMap(map);
347   }
348 
349   /**
350    * For the given Enum value {@code rank}, returns the value's
351    * {@code "EnumClass.name"}, which is used in exception and warning
352    * output.
353    */
354   private static String getLockName(Enum<?> rank) {
355     return rank.getDeclaringClass().getSimpleName() + "." + rank.name();
356   }
357 
358   /**
359    * <p>A {@code CycleDetectingLockFactory.WithExplicitOrdering} provides the
360    * additional enforcement of an application-specified ordering of lock
361    * acquisitions. The application defines the allowed ordering with an
362    * {@code Enum} whose values each correspond to a lock type. The order in
363    * which the values are declared dictates the allowed order of lock
364    * acquisition. In other words, locks corresponding to smaller values of
365    * {@link Enum#ordinal()} should only be acquired before locks with larger
366    * ordinals. Example:
367    *
368    * <pre>   {@code
369    * enum MyLockOrder {
370    *   FIRST, SECOND, THIRD;
371    * }
372    *
373    * CycleDetectingLockFactory.WithExplicitOrdering<MyLockOrder> factory =
374    *   CycleDetectingLockFactory.newInstanceWithExplicitOrdering(Policies.THROW);
375    *
376    * Lock lock1 = factory.newReentrantLock(MyLockOrder.FIRST);
377    * Lock lock2 = factory.newReentrantLock(MyLockOrder.SECOND);
378    * Lock lock3 = factory.newReentrantLock(MyLockOrder.THIRD);
379    *
380    * lock1.lock();
381    * lock3.lock();
382    * lock2.lock();  // will throw an IllegalStateException}</pre>
383    *
384    * <p>As with all locks created by instances of {@code CycleDetectingLockFactory}
385    * explicitly ordered locks participate in general cycle detection with all
386    * other cycle detecting locks, and a lock's behavior when detecting a cyclic
387    * lock acquisition is defined by the {@code Policy} of the factory that
388    * created it.
389    *
390    * <p>Note, however, that although multiple locks can be created for a given Enum
391    * value, whether it be through separate factory instances or through multiple
392    * calls to the same factory, attempting to acquire multiple locks with the
393    * same Enum value (within the same thread) will result in an
394    * IllegalStateException regardless of the factory's policy. For example:
395    *
396    * <pre>   {@code
397    * CycleDetectingLockFactory.WithExplicitOrdering<MyLockOrder> factory1 =
398    *   CycleDetectingLockFactory.newInstanceWithExplicitOrdering(...);
399    * CycleDetectingLockFactory.WithExplicitOrdering<MyLockOrder> factory2 =
400    *   CycleDetectingLockFactory.newInstanceWithExplicitOrdering(...);
401    *
402    * Lock lockA = factory1.newReentrantLock(MyLockOrder.FIRST);
403    * Lock lockB = factory1.newReentrantLock(MyLockOrder.FIRST);
404    * Lock lockC = factory2.newReentrantLock(MyLockOrder.FIRST);
405    *
406    * lockA.lock();
407    *
408    * lockB.lock();  // will throw an IllegalStateException
409    * lockC.lock();  // will throw an IllegalStateException
410    *
411    * lockA.lock();  // reentrant acquisition is okay}</pre>
412    *
413    * <p>It is the responsibility of the application to ensure that multiple lock
414    * instances with the same rank are never acquired in the same thread.
415    *
416    * @param <E> The Enum type representing the explicit lock ordering.
417    * @since 13.0
418    */
419   @Beta
420   public static final class WithExplicitOrdering<E extends Enum<E>>
421       extends CycleDetectingLockFactory {
422 
423     private final Map<E, LockGraphNode> lockGraphNodes;
424 
425     @VisibleForTesting
426     WithExplicitOrdering(
427         Policy policy, Map<E, LockGraphNode> lockGraphNodes) {
428       super(policy);
429       this.lockGraphNodes = lockGraphNodes;
430     }
431 
432     /**
433      * Equivalent to {@code newReentrantLock(rank, false)}.
434      */
435     public ReentrantLock newReentrantLock(E rank) {
436       return newReentrantLock(rank, false);
437     }
438 
439     /**
440      * Creates a {@link ReentrantLock} with the given fairness policy and rank.
441      * The values returned by {@link Enum#getDeclaringClass()} and
442      * {@link Enum#name()} are used to describe the lock in warning or
443      * exception output.
444      *
445      * @throws IllegalStateException If the factory has already created a
446      *    {@code Lock} with the specified rank.
447      */
448     public ReentrantLock newReentrantLock(E rank, boolean fair) {
449       return policy == Policies.DISABLED ? new ReentrantLock(fair)
450           : new CycleDetectingReentrantLock(lockGraphNodes.get(rank), fair);
451     }
452 
453     /**
454      * Equivalent to {@code newReentrantReadWriteLock(rank, false)}.
455      */
456     public ReentrantReadWriteLock newReentrantReadWriteLock(E rank) {
457       return newReentrantReadWriteLock(rank, false);
458     }
459 
460     /**
461      * Creates a {@link ReentrantReadWriteLock} with the given fairness policy
462      * and rank. The values returned by {@link Enum#getDeclaringClass()} and
463      * {@link Enum#name()} are used to describe the lock in warning or exception
464      * output.
465      *
466      * @throws IllegalStateException If the factory has already created a
467      *    {@code Lock} with the specified rank.
468      */
469     public ReentrantReadWriteLock newReentrantReadWriteLock(
470         E rank, boolean fair) {
471       return policy == Policies.DISABLED ? new ReentrantReadWriteLock(fair)
472           : new CycleDetectingReentrantReadWriteLock(
473               lockGraphNodes.get(rank), fair);
474     }
475   }
476 
477   //////// Implementation /////////
478 
479   private static final Logger logger = Logger.getLogger(
480       CycleDetectingLockFactory.class.getName());
481 
482   final Policy policy;
483 
484   private CycleDetectingLockFactory(Policy policy) {
485     this.policy = checkNotNull(policy);
486   }
487 
488   /**
489    * Tracks the currently acquired locks for each Thread, kept up to date by
490    * calls to {@link #aboutToAcquire(CycleDetectingLock)} and
491    * {@link #lockStateChanged(CycleDetectingLock)}.
492    */
493   // This is logically a Set, but an ArrayList is used to minimize the amount
494   // of allocation done on lock()/unlock().
495   private static final ThreadLocal<ArrayList<LockGraphNode>>
496       acquiredLocks = new ThreadLocal<ArrayList<LockGraphNode>>() {
497     @Override
498     protected ArrayList<LockGraphNode> initialValue() {
499       return Lists.<LockGraphNode>newArrayListWithCapacity(3);
500     }
501   };
502 
503   /**
504    * A Throwable used to record a stack trace that illustrates an example of
505    * a specific lock acquisition ordering. The top of the stack trace is
506    * truncated such that it starts with the acquisition of the lock in
507    * question, e.g.
508    *
509    * <pre>
510    * com...ExampleStackTrace: LockB -&gt; LockC
511    *   at com...CycleDetectingReentrantLock.lock(CycleDetectingLockFactory.java:443)
512    *   at ...
513    *   at ...
514    *   at com...MyClass.someMethodThatAcquiresLockB(MyClass.java:123)
515    * </pre>
516    */
517   private static class ExampleStackTrace extends IllegalStateException {
518 
519     static final StackTraceElement[] EMPTY_STACK_TRACE =
520         new StackTraceElement[0];
521 
522     static Set<String> EXCLUDED_CLASS_NAMES = ImmutableSet.of(
523         CycleDetectingLockFactory.class.getName(),
524         ExampleStackTrace.class.getName(),
525         LockGraphNode.class.getName());
526 
527     ExampleStackTrace(LockGraphNode node1, LockGraphNode node2) {
528       super(node1.getLockName() + " -> " + node2.getLockName());
529       StackTraceElement[] origStackTrace = getStackTrace();
530       for (int i = 0, n = origStackTrace.length; i < n; i++) {
531         if (WithExplicitOrdering.class.getName().equals(
532                 origStackTrace[i].getClassName())) {
533           // For pre-populated disallowedPriorLocks edges, omit the stack trace.
534           setStackTrace(EMPTY_STACK_TRACE);
535           break;
536         }
537         if (!EXCLUDED_CLASS_NAMES.contains(origStackTrace[i].getClassName())) {
538           setStackTrace(Arrays.copyOfRange(origStackTrace, i, n));
539           break;
540         }
541       }
542     }
543   }
544 
545   /**
546    * Represents a detected cycle in lock acquisition ordering. The exception
547    * includes a causal chain of {@code ExampleStackTrace} instances to illustrate the
548    * cycle, e.g.
549    *
550    * <pre>
551    * com....PotentialDeadlockException: Potential Deadlock from LockC -&gt; ReadWriteA
552    *   at ...
553    *   at ...
554    * Caused by: com...ExampleStackTrace: LockB -&gt; LockC
555    *   at ...
556    *   at ...
557    * Caused by: com...ExampleStackTrace: ReadWriteA -&gt; LockB
558    *   at ...
559    *   at ...
560    * </pre>
561    *
562    * <p>Instances are logged for the {@code Policies.WARN}, and thrown for
563    * {@code Policies.THROW}.
564    *
565    * @since 13.0
566    */
567   @Beta
568   public static final class PotentialDeadlockException
569       extends ExampleStackTrace {
570 
571     private final ExampleStackTrace conflictingStackTrace;
572 
573     private PotentialDeadlockException(
574         LockGraphNode node1,
575         LockGraphNode node2,
576         ExampleStackTrace conflictingStackTrace) {
577       super(node1, node2);
578       this.conflictingStackTrace = conflictingStackTrace;
579       initCause(conflictingStackTrace);
580     }
581 
582     public ExampleStackTrace getConflictingStackTrace() {
583       return conflictingStackTrace;
584     }
585 
586     /**
587      * Appends the chain of messages from the {@code conflictingStackTrace} to
588      * the original {@code message}.
589      */
590     @Override
591     public String getMessage() {
592       StringBuilder message = new StringBuilder(super.getMessage());
593       for (Throwable t = conflictingStackTrace; t != null; t = t.getCause()) {
594         message.append(", ").append(t.getMessage());
595       }
596       return message.toString();
597     }
598   }
599 
600   /**
601    * Internal Lock implementations implement the {@code CycleDetectingLock}
602    * interface, allowing the detection logic to treat all locks in the same
603    * manner.
604    */
605   private interface CycleDetectingLock {
606 
607     /** @return the {@link LockGraphNode} associated with this lock. */
608     LockGraphNode getLockGraphNode();
609 
610     /** @return {@code true} if the current thread has acquired this lock. */
611     boolean isAcquiredByCurrentThread();
612   }
613 
614   /**
615    * A {@code LockGraphNode} associated with each lock instance keeps track of
616    * the directed edges in the lock acquisition graph.
617    */
618   private static class LockGraphNode {
619 
620     /**
621      * The map tracking the locks that are known to be acquired before this
622      * lock, each associated with an example stack trace. Locks are weakly keyed
623      * to allow proper garbage collection when they are no longer referenced.
624      */
625     final Map<LockGraphNode, ExampleStackTrace> allowedPriorLocks =
626         new MapMaker().weakKeys().makeMap();
627 
628     /**
629      * The map tracking lock nodes that can cause a lock acquisition cycle if
630      * acquired before this node.
631      */
632     final Map<LockGraphNode, PotentialDeadlockException>
633         disallowedPriorLocks = new MapMaker().weakKeys().makeMap();
634 
635     final String lockName;
636 
637     LockGraphNode(String lockName) {
638       this.lockName = Preconditions.checkNotNull(lockName);
639     }
640 
641     String getLockName() {
642       return lockName;
643     }
644 
645     void checkAcquiredLocks(
646         Policy policy, List<LockGraphNode> acquiredLocks) {
647       for (int i = 0, size = acquiredLocks.size(); i < size; i++) {
648         checkAcquiredLock(policy, acquiredLocks.get(i));
649       }
650     }
651 
652     /**
653      * Checks the acquisition-ordering between {@code this}, which is about to
654      * be acquired, and the specified {@code acquiredLock}.
655      * <p>
656      * When this method returns, the {@code acquiredLock} should be in either
657      * the {@code preAcquireLocks} map, for the case in which it is safe to
658      * acquire {@code this} after the {@code acquiredLock}, or in the
659      * {@code disallowedPriorLocks} map, in which case it is not safe.
660      */
661     void checkAcquiredLock(Policy policy, LockGraphNode acquiredLock) {
662       // checkAcquiredLock() should never be invoked by a lock that has already
663       // been acquired. For unordered locks, aboutToAcquire() ensures this by
664       // checking isAcquiredByCurrentThread(). For ordered locks, however, this
665       // can happen because multiple locks may share the same LockGraphNode. In
666       // this situation, throw an IllegalStateException as defined by contract
667       // described in the documentation of WithExplicitOrdering.
668       Preconditions.checkState(
669           this != acquiredLock,
670           "Attempted to acquire multiple locks with the same rank " +
671           acquiredLock.getLockName());
672 
673       if (allowedPriorLocks.containsKey(acquiredLock)) {
674         // The acquisition ordering from "acquiredLock" to "this" has already
675         // been verified as safe. In a properly written application, this is
676         // the common case.
677         return;
678       }
679       PotentialDeadlockException previousDeadlockException =
680           disallowedPriorLocks.get(acquiredLock);
681       if (previousDeadlockException != null) {
682         // Previously determined to be an unsafe lock acquisition.
683         // Create a new PotentialDeadlockException with the same causal chain
684         // (the example cycle) as that of the cached exception.
685         PotentialDeadlockException exception = new PotentialDeadlockException(
686             acquiredLock, this,
687             previousDeadlockException.getConflictingStackTrace());
688         policy.handlePotentialDeadlock(exception);
689         return;
690       }
691       // Otherwise, it's the first time seeing this lock relationship. Look for
692       // a path from the acquiredLock to this.
693       Set<LockGraphNode> seen = Sets.newIdentityHashSet();
694       ExampleStackTrace path = acquiredLock.findPathTo(this, seen);
695 
696       if (path == null) {
697         // this can be safely acquired after the acquiredLock.
698         //
699         // Note that there is a race condition here which can result in missing
700         // a cyclic edge: it's possible for two threads to simultaneous find
701         // "safe" edges which together form a cycle. Preventing this race
702         // condition efficiently without _introducing_ deadlock is probably
703         // tricky. For now, just accept the race condition---missing a warning
704         // now and then is still better than having no deadlock detection.
705         allowedPriorLocks.put(
706             acquiredLock, new ExampleStackTrace(acquiredLock, this));
707       } else {
708         // Unsafe acquisition order detected. Create and cache a
709         // PotentialDeadlockException.
710         PotentialDeadlockException exception =
711             new PotentialDeadlockException(acquiredLock, this, path);
712         disallowedPriorLocks.put(acquiredLock, exception);
713         policy.handlePotentialDeadlock(exception);
714       }
715     }
716 
717     /**
718      * Performs a depth-first traversal of the graph edges defined by each
719      * node's {@code allowedPriorLocks} to find a path between {@code this} and
720      * the specified {@code lock}.
721      *
722      * @return If a path was found, a chained {@link ExampleStackTrace}
723      *     illustrating the path to the {@code lock}, or {@code null} if no path
724      *     was found.
725      */
726     @Nullable
727     private ExampleStackTrace findPathTo(
728         LockGraphNode node, Set<LockGraphNode> seen) {
729       if (!seen.add(this)) {
730         return null;  // Already traversed this node.
731       }
732       ExampleStackTrace found = allowedPriorLocks.get(node);
733       if (found != null) {
734         return found;  // Found a path ending at the node!
735       }
736       // Recurse the edges.
737       for (Map.Entry<LockGraphNode, ExampleStackTrace> entry :
738                allowedPriorLocks.entrySet()) {
739         LockGraphNode preAcquiredLock = entry.getKey();
740         found = preAcquiredLock.findPathTo(node, seen);
741         if (found != null) {
742           // One of this node's allowedPriorLocks found a path. Prepend an
743           // ExampleStackTrace(preAcquiredLock, this) to the returned chain of
744           // ExampleStackTraces.
745           ExampleStackTrace path =
746               new ExampleStackTrace(preAcquiredLock, this);
747           path.setStackTrace(entry.getValue().getStackTrace());
748           path.initCause(found);
749           return path;
750         }
751       }
752       return null;
753     }
754   }
755 
756   /**
757    * CycleDetectingLock implementations must call this method before attempting
758    * to acquire the lock.
759    */
760   private void aboutToAcquire(CycleDetectingLock lock) {
761     if (!lock.isAcquiredByCurrentThread()) {
762       ArrayList<LockGraphNode> acquiredLockList = acquiredLocks.get();
763       LockGraphNode node = lock.getLockGraphNode();
764       node.checkAcquiredLocks(policy, acquiredLockList);
765       acquiredLockList.add(node);
766     }
767   }
768 
769   /**
770    * CycleDetectingLock implementations must call this method in a
771    * {@code finally} clause after any attempt to change the lock state,
772    * including both lock and unlock attempts. Failure to do so can result in
773    * corrupting the acquireLocks set.
774    */
775   private void lockStateChanged(CycleDetectingLock lock) {
776     if (!lock.isAcquiredByCurrentThread()) {
777       ArrayList<LockGraphNode> acquiredLockList = acquiredLocks.get();
778       LockGraphNode node = lock.getLockGraphNode();
779       // Iterate in reverse because locks are usually locked/unlocked in a
780       // LIFO order.
781       for (int i = acquiredLockList.size() - 1; i >=0; i--) {
782         if (acquiredLockList.get(i) == node) {
783           acquiredLockList.remove(i);
784           break;
785         }
786       }
787     }
788   }
789 
790   final class CycleDetectingReentrantLock
791       extends ReentrantLock implements CycleDetectingLock {
792 
793     private final LockGraphNode lockGraphNode;
794 
795     private CycleDetectingReentrantLock(
796         LockGraphNode lockGraphNode, boolean fair) {
797       super(fair);
798       this.lockGraphNode = Preconditions.checkNotNull(lockGraphNode);
799     }
800 
801     ///// CycleDetectingLock methods. /////
802 
803     @Override
804     public LockGraphNode getLockGraphNode() {
805       return lockGraphNode;
806     }
807 
808     @Override
809     public boolean isAcquiredByCurrentThread() {
810       return isHeldByCurrentThread();
811     }
812 
813     ///// Overridden ReentrantLock methods. /////
814 
815     @Override
816     public void lock() {
817       aboutToAcquire(this);
818       try {
819         super.lock();
820       } finally {
821         lockStateChanged(this);
822       }
823     }
824 
825     @Override
826     public void lockInterruptibly() throws InterruptedException {
827       aboutToAcquire(this);
828       try {
829         super.lockInterruptibly();
830       } finally {
831         lockStateChanged(this);
832       }
833     }
834 
835     @Override
836     public boolean tryLock() {
837       aboutToAcquire(this);
838       try {
839         return super.tryLock();
840       } finally {
841         lockStateChanged(this);
842       }
843     }
844 
845     @Override
846     public boolean tryLock(long timeout, TimeUnit unit)
847         throws InterruptedException {
848       aboutToAcquire(this);
849       try {
850         return super.tryLock(timeout, unit);
851       } finally {
852         lockStateChanged(this);
853       }
854     }
855 
856     @Override
857     public void unlock() {
858       try {
859         super.unlock();
860       } finally {
861         lockStateChanged(this);
862       }
863     }
864   }
865 
866   final class CycleDetectingReentrantReadWriteLock
867       extends ReentrantReadWriteLock implements CycleDetectingLock {
868 
869     // These ReadLock/WriteLock implementations shadow those in the
870     // ReentrantReadWriteLock superclass. They are simply wrappers around the
871     // internal Sync object, so this is safe since the shadowed locks are never
872     // exposed or used.
873     private final CycleDetectingReentrantReadLock readLock;
874     private final CycleDetectingReentrantWriteLock writeLock;
875 
876     private final LockGraphNode lockGraphNode;
877 
878     private CycleDetectingReentrantReadWriteLock(
879         LockGraphNode lockGraphNode, boolean fair) {
880       super(fair);
881       this.readLock = new CycleDetectingReentrantReadLock(this);
882       this.writeLock = new CycleDetectingReentrantWriteLock(this);
883       this.lockGraphNode = Preconditions.checkNotNull(lockGraphNode);
884     }
885 
886     ///// Overridden ReentrantReadWriteLock methods. /////
887 
888     @Override
889     public ReadLock readLock() {
890       return readLock;
891     }
892 
893     @Override
894     public WriteLock writeLock() {
895       return writeLock;
896     }
897 
898     ///// CycleDetectingLock methods. /////
899 
900     @Override
901     public LockGraphNode getLockGraphNode() {
902       return lockGraphNode;
903     }
904 
905     @Override
906     public boolean isAcquiredByCurrentThread() {
907       return isWriteLockedByCurrentThread() || getReadHoldCount() > 0;
908     }
909   }
910 
911   private class CycleDetectingReentrantReadLock
912       extends ReentrantReadWriteLock.ReadLock {
913 
914     final CycleDetectingReentrantReadWriteLock readWriteLock;
915 
916     CycleDetectingReentrantReadLock(
917         CycleDetectingReentrantReadWriteLock readWriteLock) {
918       super(readWriteLock);
919       this.readWriteLock = readWriteLock;
920     }
921 
922     @Override
923     public void lock() {
924       aboutToAcquire(readWriteLock);
925       try {
926         super.lock();
927       } finally {
928         lockStateChanged(readWriteLock);
929       }
930     }
931 
932     @Override
933     public void lockInterruptibly() throws InterruptedException {
934       aboutToAcquire(readWriteLock);
935       try {
936         super.lockInterruptibly();
937       } finally {
938         lockStateChanged(readWriteLock);
939       }
940     }
941 
942     @Override
943     public boolean tryLock() {
944       aboutToAcquire(readWriteLock);
945       try {
946         return super.tryLock();
947       } finally {
948         lockStateChanged(readWriteLock);
949       }
950     }
951 
952     @Override
953     public boolean tryLock(long timeout, TimeUnit unit)
954         throws InterruptedException {
955       aboutToAcquire(readWriteLock);
956       try {
957         return super.tryLock(timeout, unit);
958       } finally {
959         lockStateChanged(readWriteLock);
960       }
961     }
962 
963     @Override
964     public void unlock() {
965       try {
966         super.unlock();
967       } finally {
968         lockStateChanged(readWriteLock);
969       }
970     }
971   }
972 
973   private class CycleDetectingReentrantWriteLock
974       extends ReentrantReadWriteLock.WriteLock {
975 
976     final CycleDetectingReentrantReadWriteLock readWriteLock;
977 
978     CycleDetectingReentrantWriteLock(
979         CycleDetectingReentrantReadWriteLock readWriteLock) {
980       super(readWriteLock);
981       this.readWriteLock = readWriteLock;
982     }
983 
984     @Override
985     public void lock() {
986       aboutToAcquire(readWriteLock);
987       try {
988         super.lock();
989       } finally {
990         lockStateChanged(readWriteLock);
991       }
992     }
993 
994     @Override
995     public void lockInterruptibly() throws InterruptedException {
996       aboutToAcquire(readWriteLock);
997       try {
998         super.lockInterruptibly();
999       } finally {
1000         lockStateChanged(readWriteLock);
1001       }
1002     }
1003 
1004     @Override
1005     public boolean tryLock() {
1006       aboutToAcquire(readWriteLock);
1007       try {
1008         return super.tryLock();
1009       } finally {
1010         lockStateChanged(readWriteLock);
1011       }
1012     }
1013 
1014     @Override
1015     public boolean tryLock(long timeout, TimeUnit unit)
1016         throws InterruptedException {
1017       aboutToAcquire(readWriteLock);
1018       try {
1019         return super.tryLock(timeout, unit);
1020       } finally {
1021         lockStateChanged(readWriteLock);
1022       }
1023     }
1024 
1025     @Override
1026     public void unlock() {
1027       try {
1028         super.unlock();
1029       } finally {
1030         lockStateChanged(readWriteLock);
1031       }
1032     }
1033   }
1034 }