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 -> LockB -> LockC 096 * LockA -> LockC -> 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 -> 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 -> ReadWriteA 537 * at ... 538 * at ... 539 * Caused by: com...ExampleStackTrace: LockB -> LockC 540 * at ... 541 * at ... 542 * Caused by: com...ExampleStackTrace: ReadWriteA -> 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 }