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 com.google.common.collect.Lists.newArrayListWithCapacity;
019import static com.google.common.collect.Sets.newIdentityHashSet;
020import static java.util.Collections.unmodifiableMap;
021import static java.util.Objects.requireNonNull;
022
023import com.google.common.annotations.GwtIncompatible;
024import com.google.common.annotations.J2ktIncompatible;
025import com.google.common.annotations.VisibleForTesting;
026import com.google.common.base.MoreObjects;
027import com.google.common.base.Preconditions;
028import com.google.common.collect.ImmutableSet;
029import com.google.common.collect.MapMaker;
030import com.google.j2objc.annotations.Weak;
031import java.util.ArrayList;
032import java.util.Arrays;
033import java.util.EnumMap;
034import java.util.List;
035import java.util.Map;
036import java.util.Map.Entry;
037import java.util.Set;
038import java.util.concurrent.ConcurrentMap;
039import java.util.concurrent.TimeUnit;
040import java.util.concurrent.locks.ReentrantLock;
041import java.util.concurrent.locks.ReentrantReadWriteLock;
042import java.util.logging.Level;
043import org.jspecify.annotations.Nullable;
044
045/**
046 * The {@code CycleDetectingLockFactory} creates {@link ReentrantLock} instances and {@link
047 * ReentrantReadWriteLock} instances that detect potential deadlock by checking for cycles in lock
048 * acquisition order.
049 *
050 * <p>Potential deadlocks detected when calling the {@code lock()}, {@code lockInterruptibly()}, or
051 * {@code tryLock()} methods will result in the execution of the {@link Policy} specified when
052 * creating the factory. The currently available policies are:
053 *
054 * <ul>
055 *   <li>DISABLED
056 *   <li>WARN
057 *   <li>THROW
058 * </ul>
059 *
060 * <p>The locks created by a factory instance will detect lock acquisition cycles with locks created
061 * by other {@code CycleDetectingLockFactory} instances (except those with {@code Policy.DISABLED}).
062 * A lock's behavior when a cycle is detected, however, is defined by the {@code Policy} of the
063 * factory that created it. This allows detection of cycles across components while delegating
064 * control over lock behavior to individual components.
065 *
066 * <p>Applications are encouraged to use a {@code CycleDetectingLockFactory} to create any locks for
067 * which external/unmanaged code is executed while the lock is held. (See caveats under
068 * <strong>Performance</strong>).
069 *
070 * <p><strong>Cycle Detection</strong>
071 *
072 * <p>Deadlocks can arise when locks are acquired in an order that forms a cycle. In a simple
073 * example involving two locks and two threads, deadlock occurs when one thread acquires Lock A, and
074 * then Lock B, while another thread acquires Lock B, and then Lock A:
075 *
076 * <pre>
077 * Thread1: acquire(LockA) --X acquire(LockB)
078 * Thread2: acquire(LockB) --X acquire(LockA)
079 * </pre>
080 *
081 * <p>Neither thread will progress because each is waiting for the other. In more complex
082 * applications, cycles can arise from interactions among more than 2 locks:
083 *
084 * <pre>
085 * Thread1: acquire(LockA) --X acquire(LockB)
086 * Thread2: acquire(LockB) --X acquire(LockC)
087 * ...
088 * ThreadN: acquire(LockN) --X acquire(LockA)
089 * </pre>
090 *
091 * <p>The implementation detects cycles by constructing a directed graph in which each lock
092 * represents a node and each edge represents an acquisition ordering between two locks.
093 *
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
101 *       to check for a cycle that reaches the lock to be acquired. If no cycle is detected, a new
102 *       "safe" 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@J2ktIncompatible
161@GwtIncompatible
162public class CycleDetectingLockFactory {
163
164  /**
165   * Encapsulates the action to be taken when a potential deadlock is encountered. Clients can use
166   * one of the predefined {@link Policies} or specify a custom implementation. Implementations must
167   * be thread-safe.
168   *
169   * @since 13.0
170   */
171  public interface Policy {
172
173    /**
174     * Called when a potential deadlock is encountered. Implementations can throw the given {@code
175     * exception} and/or execute other desired logic.
176     *
177     * <p>Note that the method will be called even upon an invocation of {@code tryLock()}. Although
178     * {@code tryLock()} technically recovers from deadlock by eventually timing out, this behavior
179     * is chosen based on the assumption that it is the application's wish to prohibit any cyclical
180     * lock acquisitions.
181     */
182    void handlePotentialDeadlock(PotentialDeadlockException exception);
183  }
184
185  /**
186   * Pre-defined {@link Policy} implementations.
187   *
188   * @since 13.0
189   */
190  public enum Policies implements Policy {
191    /**
192     * When potential deadlock is detected, this policy results in the throwing of the {@code
193     * PotentialDeadlockException} indicating the potential deadlock, which includes stack traces
194     * illustrating the cycle in lock acquisition order.
195     */
196    THROW {
197      @Override
198      public void handlePotentialDeadlock(PotentialDeadlockException e) {
199        throw e;
200      }
201    },
202
203    /**
204     * When potential deadlock is detected, this policy results in the logging of a {@link
205     * Level#SEVERE} message indicating the potential deadlock, which includes stack traces
206     * illustrating the cycle in lock acquisition order.
207     */
208    WARN {
209      @Override
210      public void handlePotentialDeadlock(PotentialDeadlockException e) {
211        logger.get().log(Level.SEVERE, "Detected potential deadlock", e);
212      }
213    },
214
215    /**
216     * Disables cycle detection. This option causes the factory to return unmodified lock
217     * implementations provided by the JDK, and is provided to allow applications to easily
218     * parameterize when cycle detection is enabled.
219     *
220     * <p>Note that locks created by a factory with this policy will <em>not</em> participate the
221     * cycle detection performed by locks created by other factories.
222     */
223    DISABLED {
224      @Override
225      public void handlePotentialDeadlock(PotentialDeadlockException e) {}
226    };
227  }
228
229  /** Creates a new factory with the specified policy. */
230  public static CycleDetectingLockFactory newInstance(Policy policy) {
231    return new CycleDetectingLockFactory(policy);
232  }
233
234  /** Equivalent to {@code newReentrantLock(lockName, false)}. */
235  public ReentrantLock newReentrantLock(String lockName) {
236    return newReentrantLock(lockName, false);
237  }
238
239  /**
240   * Creates a {@link ReentrantLock} with the given fairness policy. The {@code lockName} is used in
241   * the warning or exception output to help identify the locks involved in the detected deadlock.
242   */
243  public ReentrantLock newReentrantLock(String lockName, boolean fair) {
244    return policy == Policies.DISABLED
245        ? new ReentrantLock(fair)
246        : new CycleDetectingReentrantLock(new LockGraphNode(lockName), fair);
247  }
248
249  /** Equivalent to {@code newReentrantReadWriteLock(lockName, false)}. */
250  public ReentrantReadWriteLock newReentrantReadWriteLock(String lockName) {
251    return newReentrantReadWriteLock(lockName, false);
252  }
253
254  /**
255   * Creates a {@link ReentrantReadWriteLock} with the given fairness policy. The {@code lockName}
256   * is used in the warning or exception output to help identify the locks involved in the detected
257   * deadlock.
258   */
259  public ReentrantReadWriteLock newReentrantReadWriteLock(String lockName, boolean fair) {
260    return policy == Policies.DISABLED
261        ? new ReentrantReadWriteLock(fair)
262        : new CycleDetectingReentrantReadWriteLock(new LockGraphNode(lockName), fair);
263  }
264
265  // A static mapping from an Enum type to its set of LockGraphNodes.
266  private static final ConcurrentMap<
267          Class<? extends Enum<?>>, Map<? extends Enum<?>, LockGraphNode>>
268      lockGraphNodesPerType = new MapMaker().weakKeys().makeMap();
269
270  /** Creates a {@code CycleDetectingLockFactory.WithExplicitOrdering<E>}. */
271  public static <E extends Enum<E>> WithExplicitOrdering<E> newInstanceWithExplicitOrdering(
272      Class<E> enumClass, Policy policy) {
273    // createNodes maps each enumClass to a Map with the corresponding enum key
274    // type.
275    checkNotNull(enumClass);
276    checkNotNull(policy);
277    @SuppressWarnings("unchecked")
278    Map<E, LockGraphNode> lockGraphNodes = (Map<E, LockGraphNode>) getOrCreateNodes(enumClass);
279    return new WithExplicitOrdering<>(policy, lockGraphNodes);
280  }
281
282  @SuppressWarnings("unchecked")
283  private static <E extends Enum<E>> Map<? extends E, LockGraphNode> getOrCreateNodes(
284      Class<E> clazz) {
285    Map<E, LockGraphNode> existing = (Map<E, LockGraphNode>) lockGraphNodesPerType.get(clazz);
286    if (existing != null) {
287      return existing;
288    }
289    Map<E, LockGraphNode> created = createNodes(clazz);
290    existing = (Map<E, LockGraphNode>) lockGraphNodesPerType.putIfAbsent(clazz, created);
291    return MoreObjects.firstNonNull(existing, created);
292  }
293
294  /**
295   * For a given Enum type, creates an immutable map from each of the Enum's values to a
296   * corresponding LockGraphNode, with the {@code allowedPriorLocks} and {@code
297   * disallowedPriorLocks} prepopulated with nodes according to the natural ordering of the
298   * associated Enum values.
299   */
300  @VisibleForTesting
301  static <E extends Enum<E>> Map<E, LockGraphNode> createNodes(Class<E> clazz) {
302    EnumMap<E, LockGraphNode> map = new EnumMap<>(clazz);
303    E[] keys = clazz.getEnumConstants();
304    int numKeys = keys.length;
305    ArrayList<LockGraphNode> nodes = newArrayListWithCapacity(numKeys);
306    // Create a LockGraphNode for each enum value.
307    for (E key : keys) {
308      LockGraphNode node = new LockGraphNode(getLockName(key));
309      nodes.add(node);
310      map.put(key, node);
311    }
312    // Pre-populate all allowedPriorLocks with nodes of smaller ordinal.
313    for (int i = 1; i < numKeys; i++) {
314      nodes.get(i).checkAcquiredLocks(Policies.THROW, nodes.subList(0, i));
315    }
316    // Pre-populate all disallowedPriorLocks with nodes of larger ordinal.
317    for (int i = 0; i < numKeys - 1; i++) {
318      nodes.get(i).checkAcquiredLocks(Policies.DISABLED, nodes.subList(i + 1, numKeys));
319    }
320    return unmodifiableMap(map);
321  }
322
323  /**
324   * For the given Enum value {@code rank}, returns the value's {@code "EnumClass.name"}, which is
325   * used in exception and warning output.
326   */
327  private static String getLockName(Enum<?> rank) {
328    return rank.getDeclaringClass().getSimpleName() + "." + rank.name();
329  }
330
331  /**
332   * A {@code CycleDetectingLockFactory.WithExplicitOrdering} provides the additional enforcement of
333   * an application-specified ordering of lock acquisitions. The application defines the allowed
334   * ordering with an {@code Enum} whose values each correspond to a lock type. The order in which
335   * the values are declared dictates the allowed order of lock acquisition. In other words, locks
336   * corresponding to smaller values of {@link Enum#ordinal()} should only be acquired before locks
337   * with larger ordinals. Example:
338   *
339   * {@snippet :
340   * enum MyLockOrder {
341   *   FIRST, SECOND, THIRD;
342   * }
343   *
344   * CycleDetectingLockFactory.WithExplicitOrdering<MyLockOrder> factory =
345   *     CycleDetectingLockFactory.newInstanceWithExplicitOrdering(Policies.THROW);
346   *
347   * Lock lock1 = factory.newReentrantLock(MyLockOrder.FIRST);
348   * Lock lock2 = factory.newReentrantLock(MyLockOrder.SECOND);
349   * Lock lock3 = factory.newReentrantLock(MyLockOrder.THIRD);
350   *
351   * lock1.lock();
352   * lock3.lock();
353   * lock2.lock(); // will throw an IllegalStateException
354   * }
355   *
356   * <p>As with all locks created by instances of {@code CycleDetectingLockFactory} explicitly
357   * ordered locks participate in general cycle detection with all other cycle detecting locks, and
358   * a lock's behavior when detecting a cyclic lock acquisition is defined by the {@code Policy} of
359   * the factory that created it.
360   *
361   * <p>Note, however, that although multiple locks can be created for a given Enum value, whether
362   * it be through separate factory instances or through multiple calls to the same factory,
363   * attempting to acquire multiple locks with the same Enum value (within the same thread) will
364   * result in an IllegalStateException regardless of the factory's policy. For example:
365   *
366   * {@snippet :
367   * CycleDetectingLockFactory.WithExplicitOrdering<MyLockOrder> factory1 =
368   *     CycleDetectingLockFactory.newInstanceWithExplicitOrdering(...);
369   * CycleDetectingLockFactory.WithExplicitOrdering<MyLockOrder> factory2 =
370   *     CycleDetectingLockFactory.newInstanceWithExplicitOrdering(...);
371   *
372   * Lock lockA = factory1.newReentrantLock(MyLockOrder.FIRST);
373   * Lock lockB = factory1.newReentrantLock(MyLockOrder.FIRST);
374   * Lock lockC = factory2.newReentrantLock(MyLockOrder.FIRST);
375   *
376   * lockA.lock();
377   *
378   * lockB.lock(); // will throw an IllegalStateException
379   * lockC.lock(); // will throw an IllegalStateException
380   *
381   * lockA.lock(); // reentrant acquisition is okay
382   * }
383   *
384   * <p>It is the responsibility of the application to ensure that multiple lock instances with the
385   * same rank are never acquired in the same thread.
386   *
387   * @param <E> The Enum type representing the explicit lock ordering.
388   * @since 13.0
389   */
390  public static final class WithExplicitOrdering<E extends Enum<E>>
391      extends CycleDetectingLockFactory {
392
393    private final Map<E, LockGraphNode> lockGraphNodes;
394
395    @VisibleForTesting
396    WithExplicitOrdering(Policy policy, Map<E, LockGraphNode> lockGraphNodes) {
397      super(policy);
398      this.lockGraphNodes = lockGraphNodes;
399    }
400
401    /** Equivalent to {@code newReentrantLock(rank, false)}. */
402    public ReentrantLock newReentrantLock(E rank) {
403      return newReentrantLock(rank, false);
404    }
405
406    /**
407     * Creates a {@link ReentrantLock} with the given fairness policy and rank. The values returned
408     * by {@link Enum#getDeclaringClass()} and {@link Enum#name()} are used to describe the lock in
409     * warning or exception output.
410     *
411     * @throws IllegalStateException If the factory has already created a {@code Lock} with the
412     *     specified rank.
413     */
414    public ReentrantLock newReentrantLock(E rank, boolean fair) {
415      return policy == Policies.DISABLED
416          ? new ReentrantLock(fair)
417          // requireNonNull is safe because createNodes inserts an entry for every E.
418          // (If the caller passes `null` for the `rank` parameter, this will throw, but that's OK.)
419          : new CycleDetectingReentrantLock(requireNonNull(lockGraphNodes.get(rank)), fair);
420    }
421
422    /** Equivalent to {@code newReentrantReadWriteLock(rank, false)}. */
423    public ReentrantReadWriteLock newReentrantReadWriteLock(E rank) {
424      return newReentrantReadWriteLock(rank, false);
425    }
426
427    /**
428     * Creates a {@link ReentrantReadWriteLock} with the given fairness policy and rank. The values
429     * returned by {@link Enum#getDeclaringClass()} and {@link Enum#name()} are used to describe the
430     * lock in warning or exception output.
431     *
432     * @throws IllegalStateException If the factory has already created a {@code Lock} with the
433     *     specified rank.
434     */
435    public ReentrantReadWriteLock newReentrantReadWriteLock(E rank, boolean fair) {
436      return policy == Policies.DISABLED
437          ? new ReentrantReadWriteLock(fair)
438          // requireNonNull is safe because createNodes inserts an entry for every E.
439          // (If the caller passes `null` for the `rank` parameter, this will throw, but that's OK.)
440          : new CycleDetectingReentrantReadWriteLock(
441              requireNonNull(lockGraphNodes.get(rank)), fair);
442    }
443  }
444
445  // Implementation
446
447  private static final LazyLogger logger = new LazyLogger(CycleDetectingLockFactory.class);
448
449  final Policy policy;
450
451  private CycleDetectingLockFactory(Policy policy) {
452    this.policy = checkNotNull(policy);
453  }
454
455  /**
456   * Tracks the currently acquired locks for each Thread, kept up to date by calls to {@link
457   * #aboutToAcquire(CycleDetectingLock)} and {@link #lockStateChanged(CycleDetectingLock)}.
458   */
459  // This is logically a Set, but an ArrayList is used to minimize the amount
460  // of allocation done on lock()/unlock().
461  private static final ThreadLocal<List<LockGraphNode>> acquiredLocks =
462      new ThreadLocal<List<LockGraphNode>>() {
463        @Override
464        protected List<LockGraphNode> initialValue() {
465          return newArrayListWithCapacity(3);
466        }
467      };
468
469  /**
470   * A Throwable used to record a stack trace that illustrates an example of a specific lock
471   * acquisition ordering. The top of the stack trace is truncated such that it starts with the
472   * acquisition of the lock in question, e.g.
473   *
474   * <pre>
475   * com...ExampleStackTrace: LockB -&gt; LockC
476   *   at com...CycleDetectingReentrantLock.lock(CycleDetectingLockFactory.java:443)
477   *   at ...
478   *   at ...
479   *   at com...MyClass.someMethodThatAcquiresLockB(MyClass.java:123)
480   * </pre>
481   */
482  private static class ExampleStackTrace extends IllegalStateException {
483
484    static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
485
486    static final ImmutableSet<String> EXCLUDED_CLASS_NAMES =
487        ImmutableSet.of(
488            CycleDetectingLockFactory.class.getName(),
489            ExampleStackTrace.class.getName(),
490            LockGraphNode.class.getName());
491
492    ExampleStackTrace(LockGraphNode node1, LockGraphNode node2) {
493      super(node1.getLockName() + " -> " + node2.getLockName());
494      StackTraceElement[] origStackTrace = getStackTrace();
495      for (int i = 0, n = origStackTrace.length; i < n; i++) {
496        if (WithExplicitOrdering.class.getName().equals(origStackTrace[i].getClassName())) {
497          // For pre-populated disallowedPriorLocks edges, omit the stack trace.
498          setStackTrace(EMPTY_STACK_TRACE);
499          break;
500        }
501        if (!EXCLUDED_CLASS_NAMES.contains(origStackTrace[i].getClassName())) {
502          setStackTrace(Arrays.copyOfRange(origStackTrace, i, n));
503          break;
504        }
505      }
506    }
507  }
508
509  /**
510   * Represents a detected cycle in lock acquisition ordering. The exception includes a causal chain
511   * of {@code ExampleStackTrace} instances to illustrate the cycle, e.g.
512   *
513   * <pre>
514   * com....PotentialDeadlockException: Potential Deadlock from LockC -&gt; ReadWriteA
515   *   at ...
516   *   at ...
517   * Caused by: com...ExampleStackTrace: LockB -&gt; LockC
518   *   at ...
519   *   at ...
520   * Caused by: com...ExampleStackTrace: ReadWriteA -&gt; LockB
521   *   at ...
522   *   at ...
523   * </pre>
524   *
525   * <p>Instances are logged for the {@code Policies.WARN}, and thrown for {@code Policies.THROW}.
526   *
527   * @since 13.0
528   */
529  public static final class PotentialDeadlockException extends ExampleStackTrace {
530
531    private final ExampleStackTrace conflictingStackTrace;
532
533    private PotentialDeadlockException(
534        LockGraphNode node1, LockGraphNode node2, ExampleStackTrace conflictingStackTrace) {
535      super(node1, node2);
536      this.conflictingStackTrace = conflictingStackTrace;
537      initCause(conflictingStackTrace);
538    }
539
540    public ExampleStackTrace getConflictingStackTrace() {
541      return conflictingStackTrace;
542    }
543
544    /**
545     * Appends the chain of messages from the {@code conflictingStackTrace} to the original {@code
546     * message}.
547     */
548    @Override
549    public String getMessage() {
550      // requireNonNull is safe because ExampleStackTrace sets a non-null message.
551      StringBuilder message = new StringBuilder(requireNonNull(super.getMessage()));
552      for (Throwable t = conflictingStackTrace; t != null; t = t.getCause()) {
553        message.append(", ").append(t.getMessage());
554      }
555      return message.toString();
556    }
557  }
558
559  /**
560   * Internal Lock implementations implement the {@code CycleDetectingLock} interface, allowing the
561   * detection logic to treat all locks in the same manner.
562   */
563  private interface CycleDetectingLock {
564
565    /**
566     * @return the {@link LockGraphNode} associated with this lock.
567     */
568    LockGraphNode getLockGraphNode();
569
570    /**
571     * @return {@code true} if the current thread has acquired this lock.
572     */
573    boolean isAcquiredByCurrentThread();
574  }
575
576  /**
577   * A {@code LockGraphNode} associated with each lock instance keeps track of the directed edges in
578   * the lock acquisition graph.
579   */
580  private static final class LockGraphNode {
581
582    /**
583     * The map tracking the locks that are known to be acquired before this lock, each associated
584     * with an example stack trace. Locks are weakly keyed to allow proper garbage collection when
585     * they are no longer referenced.
586     */
587    final Map<LockGraphNode, ExampleStackTrace> allowedPriorLocks =
588        new MapMaker().weakKeys().makeMap();
589
590    /**
591     * The map tracking lock nodes that can cause a lock acquisition cycle if acquired before this
592     * node.
593     */
594    final Map<LockGraphNode, PotentialDeadlockException> disallowedPriorLocks =
595        new MapMaker().weakKeys().makeMap();
596
597    final String lockName;
598
599    LockGraphNode(String lockName) {
600      this.lockName = Preconditions.checkNotNull(lockName);
601    }
602
603    String getLockName() {
604      return lockName;
605    }
606
607    void checkAcquiredLocks(Policy policy, List<LockGraphNode> acquiredLocks) {
608      for (LockGraphNode acquiredLock : acquiredLocks) {
609        checkAcquiredLock(policy, acquiredLock);
610      }
611    }
612
613    /**
614     * Checks the acquisition-ordering between {@code this}, which is about to be acquired, and the
615     * specified {@code acquiredLock}.
616     *
617     * <p>When this method returns, the {@code acquiredLock} should be in either the {@code
618     * preAcquireLocks} map, for the case in which it is safe to acquire {@code this} after the
619     * {@code acquiredLock}, or in the {@code disallowedPriorLocks} map, in which case it is not
620     * safe.
621     */
622    void checkAcquiredLock(Policy policy, LockGraphNode acquiredLock) {
623      // checkAcquiredLock() should never be invoked by a lock that has already
624      // been acquired. For unordered locks, aboutToAcquire() ensures this by
625      // checking isAcquiredByCurrentThread(). For ordered locks, however, this
626      // can happen because multiple locks may share the same LockGraphNode. In
627      // this situation, throw an IllegalStateException as defined by contract
628      // described in the documentation of WithExplicitOrdering.
629      Preconditions.checkState(
630          this != acquiredLock,
631          "Attempted to acquire multiple locks with the same rank %s",
632          acquiredLock.getLockName());
633
634      if (allowedPriorLocks.containsKey(acquiredLock)) {
635        // The acquisition ordering from "acquiredLock" to "this" has already
636        // been verified as safe. In a properly written application, this is
637        // the common case.
638        return;
639      }
640      PotentialDeadlockException previousDeadlockException = disallowedPriorLocks.get(acquiredLock);
641      if (previousDeadlockException != null) {
642        // Previously determined to be an unsafe lock acquisition.
643        // Create a new PotentialDeadlockException with the same causal chain
644        // (the example cycle) as that of the cached exception.
645        PotentialDeadlockException exception =
646            new PotentialDeadlockException(
647                acquiredLock, this, previousDeadlockException.getConflictingStackTrace());
648        policy.handlePotentialDeadlock(exception);
649        return;
650      }
651      // Otherwise, it's the first time seeing this lock relationship. Look for
652      // a path from the acquiredLock to this.
653      Set<LockGraphNode> seen = newIdentityHashSet();
654      ExampleStackTrace path = acquiredLock.findPathTo(this, seen);
655
656      if (path == null) {
657        // this can be safely acquired after the acquiredLock.
658        //
659        // Note that there is a race condition here which can result in missing
660        // a cyclic edge: it's possible for two threads to simultaneous find
661        // "safe" edges which together form a cycle. Preventing this race
662        // condition efficiently without _introducing_ deadlock is probably
663        // tricky. For now, just accept the race condition---missing a warning
664        // now and then is still better than having no deadlock detection.
665        allowedPriorLocks.put(acquiredLock, new ExampleStackTrace(acquiredLock, this));
666      } else {
667        // Unsafe acquisition order detected. Create and cache a
668        // PotentialDeadlockException.
669        PotentialDeadlockException exception =
670            new PotentialDeadlockException(acquiredLock, this, path);
671        disallowedPriorLocks.put(acquiredLock, exception);
672        policy.handlePotentialDeadlock(exception);
673      }
674    }
675
676    /**
677     * Performs a depth-first traversal of the graph edges defined by each node's {@code
678     * allowedPriorLocks} to find a path between {@code this} and the specified {@code lock}.
679     *
680     * @return If a path was found, a chained {@link ExampleStackTrace} illustrating the path to the
681     *     {@code lock}, or {@code null} if no path was found.
682     */
683    private @Nullable ExampleStackTrace findPathTo(LockGraphNode node, Set<LockGraphNode> seen) {
684      if (!seen.add(this)) {
685        return null; // Already traversed this node.
686      }
687      ExampleStackTrace found = allowedPriorLocks.get(node);
688      if (found != null) {
689        return found; // Found a path ending at the node!
690      }
691      // Recurse the edges.
692      for (Entry<LockGraphNode, ExampleStackTrace> entry : allowedPriorLocks.entrySet()) {
693        LockGraphNode preAcquiredLock = entry.getKey();
694        found = preAcquiredLock.findPathTo(node, seen);
695        if (found != null) {
696          // One of this node's allowedPriorLocks found a path. Prepend an
697          // ExampleStackTrace(preAcquiredLock, this) to the returned chain of
698          // ExampleStackTraces.
699          ExampleStackTrace path = new ExampleStackTrace(preAcquiredLock, this);
700          path.setStackTrace(entry.getValue().getStackTrace());
701          path.initCause(found);
702          return path;
703        }
704      }
705      return null;
706    }
707  }
708
709  /**
710   * CycleDetectingLock implementations must call this method before attempting to acquire the lock.
711   */
712  private void aboutToAcquire(CycleDetectingLock lock) {
713    if (!lock.isAcquiredByCurrentThread()) {
714      // requireNonNull accommodates Android's @RecentlyNullable annotation on ThreadLocal.get
715      List<LockGraphNode> acquiredLockList = requireNonNull(acquiredLocks.get());
716      LockGraphNode node = lock.getLockGraphNode();
717      node.checkAcquiredLocks(policy, acquiredLockList);
718      acquiredLockList.add(node);
719    }
720  }
721
722  /**
723   * CycleDetectingLock implementations must call this method in a {@code finally} clause after any
724   * attempt to change the lock state, including both lock and unlock attempts. Failure to do so can
725   * result in corrupting the acquireLocks set.
726   */
727  private static void lockStateChanged(CycleDetectingLock lock) {
728    if (!lock.isAcquiredByCurrentThread()) {
729      // requireNonNull accommodates Android's @RecentlyNullable annotation on ThreadLocal.get
730      List<LockGraphNode> acquiredLockList = requireNonNull(acquiredLocks.get());
731      LockGraphNode node = lock.getLockGraphNode();
732      // Iterate in reverse because locks are usually locked/unlocked in a
733      // LIFO order.
734      for (int i = acquiredLockList.size() - 1; i >= 0; i--) {
735        if (acquiredLockList.get(i) == node) {
736          acquiredLockList.remove(i);
737          break;
738        }
739      }
740    }
741  }
742
743  private final class CycleDetectingReentrantLock extends ReentrantLock
744      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  private 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 final 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 final 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}