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