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