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