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