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