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