1 /* 2 * Copyright (C) 2011 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.util.concurrent; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static org.junit.Assert.assertThrows; 21 22 import com.google.common.base.Joiner; 23 import com.google.common.util.concurrent.CycleDetectingLockFactory.Policies; 24 import com.google.common.util.concurrent.CycleDetectingLockFactory.Policy; 25 import com.google.common.util.concurrent.CycleDetectingLockFactory.PotentialDeadlockException; 26 import java.util.concurrent.CountDownLatch; 27 import java.util.concurrent.TimeUnit; 28 import java.util.concurrent.locks.Lock; 29 import java.util.concurrent.locks.ReentrantLock; 30 import java.util.concurrent.locks.ReentrantReadWriteLock; 31 import junit.framework.TestCase; 32 33 /** 34 * Unittests for {@link CycleDetectingLockFactory}. 35 * 36 * @author Darick Tong 37 */ 38 public class CycleDetectingLockFactoryTest extends TestCase { 39 40 private ReentrantLock lockA; 41 private ReentrantLock lockB; 42 private ReentrantLock lockC; 43 private ReentrantReadWriteLock.ReadLock readLockA; 44 private ReentrantReadWriteLock.ReadLock readLockB; 45 private ReentrantReadWriteLock.ReadLock readLockC; 46 private ReentrantReadWriteLock.WriteLock writeLockA; 47 private ReentrantReadWriteLock.WriteLock writeLockB; 48 private ReentrantReadWriteLock.WriteLock writeLockC; 49 private ReentrantLock lock1; 50 private ReentrantLock lock2; 51 private ReentrantLock lock3; 52 private ReentrantLock lock01; 53 private ReentrantLock lock02; 54 private ReentrantLock lock03; 55 56 @Override setUp()57 protected void setUp() throws Exception { 58 super.setUp(); 59 CycleDetectingLockFactory factory = CycleDetectingLockFactory.newInstance(Policies.THROW); 60 lockA = factory.newReentrantLock("LockA"); 61 lockB = factory.newReentrantLock("LockB"); 62 lockC = factory.newReentrantLock("LockC"); 63 ReentrantReadWriteLock readWriteLockA = factory.newReentrantReadWriteLock("ReadWriteA"); 64 ReentrantReadWriteLock readWriteLockB = factory.newReentrantReadWriteLock("ReadWriteB"); 65 ReentrantReadWriteLock readWriteLockC = factory.newReentrantReadWriteLock("ReadWriteC"); 66 readLockA = readWriteLockA.readLock(); 67 readLockB = readWriteLockB.readLock(); 68 readLockC = readWriteLockC.readLock(); 69 writeLockA = readWriteLockA.writeLock(); 70 writeLockB = readWriteLockB.writeLock(); 71 writeLockC = readWriteLockC.writeLock(); 72 73 CycleDetectingLockFactory.WithExplicitOrdering<MyOrder> factory2 = 74 newInstanceWithExplicitOrdering(MyOrder.class, Policies.THROW); 75 lock1 = factory2.newReentrantLock(MyOrder.FIRST); 76 lock2 = factory2.newReentrantLock(MyOrder.SECOND); 77 lock3 = factory2.newReentrantLock(MyOrder.THIRD); 78 79 CycleDetectingLockFactory.WithExplicitOrdering<OtherOrder> factory3 = 80 newInstanceWithExplicitOrdering(OtherOrder.class, Policies.THROW); 81 lock01 = factory3.newReentrantLock(OtherOrder.FIRST); 82 lock02 = factory3.newReentrantLock(OtherOrder.SECOND); 83 lock03 = factory3.newReentrantLock(OtherOrder.THIRD); 84 } 85 86 // In the unittest, create each ordered factory with its own set of lock 87 // graph nodes (as opposed to using the static per-Enum map) to avoid 88 // conflicts across different test runs. 89 private <E extends Enum<E>> newInstanceWithExplicitOrdering( Class<E> enumClass, Policy policy)90 CycleDetectingLockFactory.WithExplicitOrdering<E> newInstanceWithExplicitOrdering( 91 Class<E> enumClass, Policy policy) { 92 return new CycleDetectingLockFactory.WithExplicitOrdering<E>( 93 policy, CycleDetectingLockFactory.createNodes(enumClass)); 94 } 95 testDeadlock_twoLocks()96 public void testDeadlock_twoLocks() { 97 // Establish an acquisition order of lockA -> lockB. 98 lockA.lock(); 99 lockB.lock(); 100 lockA.unlock(); 101 lockB.unlock(); 102 103 // The opposite order should fail (Policies.THROW). 104 PotentialDeadlockException firstException = null; 105 lockB.lock(); 106 PotentialDeadlockException expected = 107 assertThrows(PotentialDeadlockException.class, () -> lockA.lock()); 108 checkMessage(expected, "LockB -> LockA", "LockA -> LockB"); 109 firstException = expected; 110 // Second time should also fail, with a cached causal chain. 111 expected = assertThrows(PotentialDeadlockException.class, () -> lockA.lock()); 112 checkMessage(expected, "LockB -> LockA", "LockA -> LockB"); 113 // The causal chain should be cached. 114 assertSame(firstException.getCause(), expected.getCause()); 115 // lockA should work after lockB is released. 116 lockB.unlock(); 117 lockA.lock(); 118 } 119 120 // Tests transitive deadlock detection. testDeadlock_threeLocks()121 public void testDeadlock_threeLocks() { 122 // Establish an ordering from lockA -> lockB. 123 lockA.lock(); 124 lockB.lock(); 125 lockB.unlock(); 126 lockA.unlock(); 127 128 // Establish an ordering from lockB -> lockC. 129 lockB.lock(); 130 lockC.lock(); 131 lockB.unlock(); 132 133 // lockC -> lockA should fail. 134 PotentialDeadlockException expected = 135 assertThrows(PotentialDeadlockException.class, () -> lockA.lock()); 136 checkMessage(expected, "LockC -> LockA", "LockB -> LockC", "LockA -> LockB"); 137 } 138 testReentrancy_noDeadlock()139 public void testReentrancy_noDeadlock() { 140 lockA.lock(); 141 lockB.lock(); 142 lockA.lock(); // Should not assert on lockB -> reentrant(lockA) 143 } 144 testExplicitOrdering_noViolations()145 public void testExplicitOrdering_noViolations() { 146 lock1.lock(); 147 lock3.lock(); 148 lock3.unlock(); 149 lock2.lock(); 150 lock3.lock(); 151 } 152 testExplicitOrdering_violations()153 public void testExplicitOrdering_violations() { 154 lock3.lock(); 155 PotentialDeadlockException expected = 156 assertThrows(PotentialDeadlockException.class, () -> lock2.lock()); 157 checkMessage(expected, "MyOrder.THIRD -> MyOrder.SECOND"); 158 159 expected = assertThrows(PotentialDeadlockException.class, () -> lock1.lock()); 160 checkMessage(expected, "MyOrder.THIRD -> MyOrder.FIRST"); 161 162 lock3.unlock(); 163 lock2.lock(); 164 165 expected = assertThrows(PotentialDeadlockException.class, () -> lock1.lock()); 166 checkMessage(expected, "MyOrder.SECOND -> MyOrder.FIRST"); 167 } 168 testDifferentOrderings_noViolations()169 public void testDifferentOrderings_noViolations() { 170 lock3.lock(); // MyOrder, ordinal() == 3 171 lock01.lock(); // OtherOrder, ordinal() == 1 172 } 173 testExplicitOrderings_generalCycleDetection()174 public void testExplicitOrderings_generalCycleDetection() { 175 lock3.lock(); // MyOrder, ordinal() == 3 176 lock01.lock(); // OtherOrder, ordinal() == 1 177 178 lock3.unlock(); 179 PotentialDeadlockException expected = 180 assertThrows(PotentialDeadlockException.class, () -> lock3.lock()); 181 checkMessage( 182 expected, "OtherOrder.FIRST -> MyOrder.THIRD", "MyOrder.THIRD -> OtherOrder.FIRST"); 183 lockA.lock(); 184 lock01.unlock(); 185 lockB.lock(); 186 lockA.unlock(); 187 188 expected = assertThrows(PotentialDeadlockException.class, () -> lock01.lock()); 189 checkMessage( 190 expected, "LockB -> OtherOrder.FIRST", "LockA -> LockB", "OtherOrder.FIRST -> LockA"); 191 } 192 testExplicitOrdering_cycleWithUnorderedLock()193 public void testExplicitOrdering_cycleWithUnorderedLock() { 194 Lock myLock = CycleDetectingLockFactory.newInstance(Policies.THROW).newReentrantLock("MyLock"); 195 lock03.lock(); 196 myLock.lock(); 197 lock03.unlock(); 198 199 PotentialDeadlockException expected = 200 assertThrows(PotentialDeadlockException.class, () -> lock01.lock()); 201 checkMessage( 202 expected, 203 "MyLock -> OtherOrder.FIRST", 204 "OtherOrder.THIRD -> MyLock", 205 "OtherOrder.FIRST -> OtherOrder.THIRD"); 206 } 207 testExplicitOrdering_reentrantAcquisition()208 public void testExplicitOrdering_reentrantAcquisition() { 209 CycleDetectingLockFactory.WithExplicitOrdering<OtherOrder> factory = 210 newInstanceWithExplicitOrdering(OtherOrder.class, Policies.THROW); 211 Lock lockA = factory.newReentrantReadWriteLock(OtherOrder.FIRST).readLock(); 212 Lock lockB = factory.newReentrantLock(OtherOrder.SECOND); 213 214 lockA.lock(); 215 lockA.lock(); 216 lockB.lock(); 217 lockB.lock(); 218 lockA.unlock(); 219 lockA.unlock(); 220 lockB.unlock(); 221 lockB.unlock(); 222 } 223 testExplicitOrdering_acquiringMultipleLocksWithSameRank()224 public void testExplicitOrdering_acquiringMultipleLocksWithSameRank() { 225 CycleDetectingLockFactory.WithExplicitOrdering<OtherOrder> factory = 226 newInstanceWithExplicitOrdering(OtherOrder.class, Policies.THROW); 227 Lock lockA = factory.newReentrantLock(OtherOrder.FIRST); 228 Lock lockB = factory.newReentrantReadWriteLock(OtherOrder.FIRST).readLock(); 229 230 lockA.lock(); 231 assertThrows(IllegalStateException.class, () -> lockB.lock()); 232 233 lockA.unlock(); 234 lockB.lock(); 235 } 236 testReadLock_deadlock()237 public void testReadLock_deadlock() { 238 readLockA.lock(); // Establish an ordering from readLockA -> lockB. 239 lockB.lock(); 240 lockB.unlock(); 241 readLockA.unlock(); 242 243 lockB.lock(); 244 PotentialDeadlockException expected = 245 assertThrows(PotentialDeadlockException.class, () -> readLockA.lock()); 246 checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB"); 247 } 248 testReadLock_transitive()249 public void testReadLock_transitive() { 250 readLockA.lock(); // Establish an ordering from readLockA -> lockB. 251 lockB.lock(); 252 lockB.unlock(); 253 readLockA.unlock(); 254 255 // Establish an ordering from lockB -> readLockC. 256 lockB.lock(); 257 readLockC.lock(); 258 lockB.unlock(); 259 readLockC.unlock(); 260 261 // readLockC -> readLockA 262 readLockC.lock(); 263 PotentialDeadlockException expected = 264 assertThrows(PotentialDeadlockException.class, () -> readLockA.lock()); 265 checkMessage( 266 expected, "ReadWriteC -> ReadWriteA", "LockB -> ReadWriteC", "ReadWriteA -> LockB"); 267 } 268 testWriteLock_threeLockDeadLock()269 public void testWriteLock_threeLockDeadLock() { 270 // Establish an ordering from writeLockA -> writeLockB. 271 writeLockA.lock(); 272 writeLockB.lock(); 273 writeLockB.unlock(); 274 writeLockA.unlock(); 275 276 // Establish an ordering from writeLockB -> writeLockC. 277 writeLockB.lock(); 278 writeLockC.lock(); 279 writeLockB.unlock(); 280 281 // writeLockC -> writeLockA should fail. 282 PotentialDeadlockException expected = 283 assertThrows(PotentialDeadlockException.class, () -> writeLockA.lock()); 284 checkMessage( 285 expected, 286 "ReadWriteC -> ReadWriteA", 287 "ReadWriteB -> ReadWriteC", 288 "ReadWriteA -> ReadWriteB"); 289 } 290 testWriteToReadLockDowngrading()291 public void testWriteToReadLockDowngrading() { 292 writeLockA.lock(); // writeLockA downgrades to readLockA 293 readLockA.lock(); 294 writeLockA.unlock(); 295 296 lockB.lock(); // readLockA -> lockB 297 readLockA.unlock(); 298 299 // lockB -> writeLockA should fail 300 PotentialDeadlockException expected = 301 assertThrows(PotentialDeadlockException.class, () -> writeLockA.lock()); 302 checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB"); 303 } 304 testReadWriteLockDeadlock()305 public void testReadWriteLockDeadlock() { 306 writeLockA.lock(); // Establish an ordering from writeLockA -> lockB 307 lockB.lock(); 308 writeLockA.unlock(); 309 lockB.unlock(); 310 311 // lockB -> readLockA should fail. 312 lockB.lock(); 313 PotentialDeadlockException expected = 314 assertThrows(PotentialDeadlockException.class, () -> readLockA.lock()); 315 checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB"); 316 } 317 testReadWriteLockDeadlock_transitive()318 public void testReadWriteLockDeadlock_transitive() { 319 readLockA.lock(); // Establish an ordering from readLockA -> lockB 320 lockB.lock(); 321 readLockA.unlock(); 322 lockB.unlock(); 323 324 // Establish an ordering from lockB -> lockC 325 lockB.lock(); 326 lockC.lock(); 327 lockB.unlock(); 328 lockC.unlock(); 329 330 // lockC -> writeLockA should fail. 331 lockC.lock(); 332 PotentialDeadlockException expected = 333 assertThrows(PotentialDeadlockException.class, () -> writeLockA.lock()); 334 checkMessage(expected, "LockC -> ReadWriteA", "LockB -> LockC", "ReadWriteA -> LockB"); 335 } 336 testReadWriteLockDeadlock_treatedEquivalently()337 public void testReadWriteLockDeadlock_treatedEquivalently() { 338 readLockA.lock(); // readLockA -> writeLockB 339 writeLockB.lock(); 340 readLockA.unlock(); 341 writeLockB.unlock(); 342 343 // readLockB -> writeLockA should fail. 344 readLockB.lock(); 345 PotentialDeadlockException expected = 346 assertThrows(PotentialDeadlockException.class, () -> writeLockA.lock()); 347 checkMessage(expected, "ReadWriteB -> ReadWriteA", "ReadWriteA -> ReadWriteB"); 348 } 349 testDifferentLockFactories()350 public void testDifferentLockFactories() { 351 CycleDetectingLockFactory otherFactory = CycleDetectingLockFactory.newInstance(Policies.WARN); 352 ReentrantLock lockD = otherFactory.newReentrantLock("LockD"); 353 354 // lockA -> lockD 355 lockA.lock(); 356 lockD.lock(); 357 lockA.unlock(); 358 lockD.unlock(); 359 360 // lockD -> lockA should fail even though lockD is from a different factory. 361 lockD.lock(); 362 PotentialDeadlockException expected = 363 assertThrows(PotentialDeadlockException.class, () -> lockA.lock()); 364 checkMessage(expected, "LockD -> LockA", "LockA -> LockD"); 365 } 366 testDifferentLockFactories_policyExecution()367 public void testDifferentLockFactories_policyExecution() { 368 CycleDetectingLockFactory otherFactory = CycleDetectingLockFactory.newInstance(Policies.WARN); 369 ReentrantLock lockD = otherFactory.newReentrantLock("LockD"); 370 371 // lockD -> lockA 372 lockD.lock(); 373 lockA.lock(); 374 lockA.unlock(); 375 lockD.unlock(); 376 377 // lockA -> lockD should warn but otherwise succeed because lockD was 378 // created by a factory with the WARN policy. 379 lockA.lock(); 380 lockD.lock(); 381 } 382 testReentrantLock_tryLock()383 public void testReentrantLock_tryLock() throws Exception { 384 LockingThread thread = new LockingThread(lockA); 385 thread.start(); 386 387 thread.waitUntilHoldingLock(); 388 assertFalse(lockA.tryLock()); 389 390 thread.releaseLockAndFinish(); 391 assertTrue(lockA.tryLock()); 392 } 393 testReentrantWriteLock_tryLock()394 public void testReentrantWriteLock_tryLock() throws Exception { 395 LockingThread thread = new LockingThread(writeLockA); 396 thread.start(); 397 398 thread.waitUntilHoldingLock(); 399 assertFalse(writeLockA.tryLock()); 400 assertFalse(readLockA.tryLock()); 401 402 thread.releaseLockAndFinish(); 403 assertTrue(writeLockA.tryLock()); 404 assertTrue(readLockA.tryLock()); 405 } 406 testReentrantReadLock_tryLock()407 public void testReentrantReadLock_tryLock() throws Exception { 408 LockingThread thread = new LockingThread(readLockA); 409 thread.start(); 410 411 thread.waitUntilHoldingLock(); 412 assertFalse(writeLockA.tryLock()); 413 assertTrue(readLockA.tryLock()); 414 readLockA.unlock(); 415 416 thread.releaseLockAndFinish(); 417 assertTrue(writeLockA.tryLock()); 418 assertTrue(readLockA.tryLock()); 419 } 420 421 private static class LockingThread extends Thread { 422 final CountDownLatch locked = new CountDownLatch(1); 423 final CountDownLatch finishLatch = new CountDownLatch(1); 424 final Lock lock; 425 LockingThread(Lock lock)426 LockingThread(Lock lock) { 427 this.lock = lock; 428 } 429 430 @Override run()431 public void run() { 432 lock.lock(); 433 try { 434 locked.countDown(); 435 finishLatch.await(1, TimeUnit.MINUTES); 436 } catch (InterruptedException e) { 437 fail(e.toString()); 438 } finally { 439 lock.unlock(); 440 } 441 } 442 waitUntilHoldingLock()443 void waitUntilHoldingLock() throws InterruptedException { 444 locked.await(1, TimeUnit.MINUTES); 445 } 446 releaseLockAndFinish()447 void releaseLockAndFinish() throws InterruptedException { 448 finishLatch.countDown(); 449 this.join(10000); 450 assertFalse(this.isAlive()); 451 } 452 } 453 testReentrantReadWriteLock_implDoesNotExposeShadowedLocks()454 public void testReentrantReadWriteLock_implDoesNotExposeShadowedLocks() { 455 assertEquals( 456 "Unexpected number of public methods in ReentrantReadWriteLock. " 457 + "The correctness of CycleDetectingReentrantReadWriteLock depends on " 458 + "the fact that the shadowed ReadLock and WriteLock are never used or " 459 + "exposed by the superclass implementation. If the implementation has " 460 + "changed, the code must be re-inspected to ensure that the " 461 + "assumption is still valid.", 462 24, 463 ReentrantReadWriteLock.class.getMethods().length); 464 } 465 466 private enum MyOrder { 467 FIRST, 468 SECOND, 469 THIRD; 470 } 471 472 private enum OtherOrder { 473 FIRST, 474 SECOND, 475 THIRD; 476 } 477 478 // Given a sequence of lock acquisition descriptions 479 // (e.g. "LockA -> LockB", "LockB -> LockC", ...) 480 // Checks that the exception.getMessage() matches a regex of the form: 481 // "LockA -> LockB \b.*\b LockB -> LockC \b.*\b LockC -> LockA" checkMessage(IllegalStateException exception, String... expectedLockCycle)482 private void checkMessage(IllegalStateException exception, String... expectedLockCycle) { 483 String regex = Joiner.on("\\b.*\\b").join(expectedLockCycle); 484 assertThat(exception).hasMessageThat().containsMatch(regex); 485 } 486 } 487