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