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