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