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