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