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 20 import com.google.common.base.Joiner; 21 import com.google.common.util.concurrent.CycleDetectingLockFactory.Policies; 22 import com.google.common.util.concurrent.CycleDetectingLockFactory.Policy; 23 import com.google.common.util.concurrent.CycleDetectingLockFactory.PotentialDeadlockException; 24 import java.util.concurrent.CountDownLatch; 25 import java.util.concurrent.TimeUnit; 26 import java.util.concurrent.locks.Lock; 27 import java.util.concurrent.locks.ReentrantLock; 28 import java.util.concurrent.locks.ReentrantReadWriteLock; 29 import java.util.regex.Matcher; 30 import java.util.regex.Pattern; 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 try { 107 lockA.lock(); 108 fail("Expected PotentialDeadlockException"); 109 } catch (PotentialDeadlockException expected) { 110 checkMessage(expected, "LockB -> LockA", "LockA -> LockB"); 111 firstException = expected; 112 } 113 114 // Second time should also fail, with a cached causal chain. 115 try { 116 lockA.lock(); 117 fail("Expected PotentialDeadlockException"); 118 } catch (PotentialDeadlockException expected) { 119 checkMessage(expected, "LockB -> LockA", "LockA -> LockB"); 120 // The causal chain should be cached. 121 assertSame(firstException.getCause(), expected.getCause()); 122 } 123 124 // lockA should work after lockB is released. 125 lockB.unlock(); 126 lockA.lock(); 127 } 128 129 // Tests transitive deadlock detection. testDeadlock_threeLocks()130 public void testDeadlock_threeLocks() { 131 // Establish an ordering from lockA -> lockB. 132 lockA.lock(); 133 lockB.lock(); 134 lockB.unlock(); 135 lockA.unlock(); 136 137 // Establish an ordering from lockB -> lockC. 138 lockB.lock(); 139 lockC.lock(); 140 lockB.unlock(); 141 142 // lockC -> lockA should fail. 143 try { 144 lockA.lock(); 145 fail("Expected PotentialDeadlockException"); 146 } catch (PotentialDeadlockException expected) { 147 checkMessage(expected, "LockC -> LockA", "LockB -> LockC", "LockA -> LockB"); 148 } 149 } 150 testReentrancy_noDeadlock()151 public void testReentrancy_noDeadlock() { 152 lockA.lock(); 153 lockB.lock(); 154 lockA.lock(); // Should not assert on lockB -> reentrant(lockA) 155 } 156 testExplicitOrdering_noViolations()157 public void testExplicitOrdering_noViolations() { 158 lock1.lock(); 159 lock3.lock(); 160 lock3.unlock(); 161 lock2.lock(); 162 lock3.lock(); 163 } 164 testExplicitOrdering_violations()165 public void testExplicitOrdering_violations() { 166 lock3.lock(); 167 try { 168 lock2.lock(); 169 fail("Expected PotentialDeadlockException"); 170 } catch (PotentialDeadlockException expected) { 171 checkMessage(expected, "MyOrder.THIRD -> MyOrder.SECOND"); 172 } 173 174 try { 175 lock1.lock(); 176 fail("Expected PotentialDeadlockException"); 177 } catch (PotentialDeadlockException expected) { 178 checkMessage(expected, "MyOrder.THIRD -> MyOrder.FIRST"); 179 } 180 181 lock3.unlock(); 182 lock2.lock(); 183 184 try { 185 lock1.lock(); 186 fail("Expected PotentialDeadlockException"); 187 } catch (PotentialDeadlockException expected) { 188 checkMessage(expected, "MyOrder.SECOND -> MyOrder.FIRST"); 189 } 190 } 191 testDifferentOrderings_noViolations()192 public void testDifferentOrderings_noViolations() { 193 lock3.lock(); // MyOrder, ordinal() == 3 194 lock01.lock(); // OtherOrder, ordinal() == 1 195 } 196 testExplicitOrderings_generalCycleDetection()197 public void testExplicitOrderings_generalCycleDetection() { 198 lock3.lock(); // MyOrder, ordinal() == 3 199 lock01.lock(); // OtherOrder, ordinal() == 1 200 201 lock3.unlock(); 202 try { 203 lock3.lock(); 204 fail("Expected PotentialDeadlockException"); 205 } catch (PotentialDeadlockException expected) { 206 checkMessage( 207 expected, "OtherOrder.FIRST -> MyOrder.THIRD", "MyOrder.THIRD -> OtherOrder.FIRST"); 208 } 209 210 lockA.lock(); 211 lock01.unlock(); 212 lockB.lock(); 213 lockA.unlock(); 214 215 try { 216 lock01.lock(); 217 fail("Expected PotentialDeadlockException"); 218 } catch (PotentialDeadlockException expected) { 219 checkMessage( 220 expected, "LockB -> OtherOrder.FIRST", "LockA -> LockB", "OtherOrder.FIRST -> LockA"); 221 } 222 } 223 testExplicitOrdering_cycleWithUnorderedLock()224 public void testExplicitOrdering_cycleWithUnorderedLock() { 225 Lock myLock = CycleDetectingLockFactory.newInstance(Policies.THROW).newReentrantLock("MyLock"); 226 lock03.lock(); 227 myLock.lock(); 228 lock03.unlock(); 229 230 try { 231 lock01.lock(); 232 fail("Expected PotentialDeadlockException"); 233 } catch (PotentialDeadlockException expected) { 234 checkMessage( 235 expected, 236 "MyLock -> OtherOrder.FIRST", 237 "OtherOrder.THIRD -> MyLock", 238 "OtherOrder.FIRST -> OtherOrder.THIRD"); 239 } 240 } 241 testExplicitOrdering_reentrantAcquisition()242 public void testExplicitOrdering_reentrantAcquisition() { 243 CycleDetectingLockFactory.WithExplicitOrdering<OtherOrder> factory = 244 newInstanceWithExplicitOrdering(OtherOrder.class, Policies.THROW); 245 Lock lockA = factory.newReentrantReadWriteLock(OtherOrder.FIRST).readLock(); 246 Lock lockB = factory.newReentrantLock(OtherOrder.SECOND); 247 248 lockA.lock(); 249 lockA.lock(); 250 lockB.lock(); 251 lockB.lock(); 252 lockA.unlock(); 253 lockA.unlock(); 254 lockB.unlock(); 255 lockB.unlock(); 256 } 257 testExplicitOrdering_acquiringMultipleLocksWithSameRank()258 public void testExplicitOrdering_acquiringMultipleLocksWithSameRank() { 259 CycleDetectingLockFactory.WithExplicitOrdering<OtherOrder> factory = 260 newInstanceWithExplicitOrdering(OtherOrder.class, Policies.THROW); 261 Lock lockA = factory.newReentrantLock(OtherOrder.FIRST); 262 Lock lockB = factory.newReentrantReadWriteLock(OtherOrder.FIRST).readLock(); 263 264 lockA.lock(); 265 try { 266 lockB.lock(); 267 fail("Expected IllegalStateException"); 268 } catch (IllegalStateException expected) { 269 } 270 271 lockA.unlock(); 272 lockB.lock(); 273 } 274 testReadLock_deadlock()275 public void testReadLock_deadlock() { 276 readLockA.lock(); // Establish an ordering from readLockA -> lockB. 277 lockB.lock(); 278 lockB.unlock(); 279 readLockA.unlock(); 280 281 lockB.lock(); 282 try { 283 readLockA.lock(); 284 fail("Expected PotentialDeadlockException"); 285 } catch (PotentialDeadlockException expected) { 286 checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB"); 287 } 288 } 289 testReadLock_transitive()290 public void testReadLock_transitive() { 291 readLockA.lock(); // Establish an ordering from readLockA -> lockB. 292 lockB.lock(); 293 lockB.unlock(); 294 readLockA.unlock(); 295 296 // Establish an ordering from lockB -> readLockC. 297 lockB.lock(); 298 readLockC.lock(); 299 lockB.unlock(); 300 readLockC.unlock(); 301 302 // readLockC -> readLockA 303 readLockC.lock(); 304 try { 305 readLockA.lock(); 306 fail("Expected PotentialDeadlockException"); 307 } catch (PotentialDeadlockException expected) { 308 checkMessage( 309 expected, "ReadWriteC -> ReadWriteA", "LockB -> ReadWriteC", "ReadWriteA -> LockB"); 310 } 311 } 312 testWriteLock_threeLockDeadLock()313 public void testWriteLock_threeLockDeadLock() { 314 // Establish an ordering from writeLockA -> writeLockB. 315 writeLockA.lock(); 316 writeLockB.lock(); 317 writeLockB.unlock(); 318 writeLockA.unlock(); 319 320 // Establish an ordering from writeLockB -> writeLockC. 321 writeLockB.lock(); 322 writeLockC.lock(); 323 writeLockB.unlock(); 324 325 // writeLockC -> writeLockA should fail. 326 try { 327 writeLockA.lock(); 328 fail("Expected PotentialDeadlockException"); 329 } catch (PotentialDeadlockException expected) { 330 checkMessage( 331 expected, 332 "ReadWriteC -> ReadWriteA", 333 "ReadWriteB -> ReadWriteC", 334 "ReadWriteA -> ReadWriteB"); 335 } 336 } 337 testWriteToReadLockDowngrading()338 public void testWriteToReadLockDowngrading() { 339 writeLockA.lock(); // writeLockA downgrades to readLockA 340 readLockA.lock(); 341 writeLockA.unlock(); 342 343 lockB.lock(); // readLockA -> lockB 344 readLockA.unlock(); 345 346 // lockB -> writeLockA should fail 347 try { 348 writeLockA.lock(); 349 fail("Expected PotentialDeadlockException"); 350 } catch (PotentialDeadlockException expected) { 351 checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB"); 352 } 353 } 354 testReadWriteLockDeadlock()355 public void testReadWriteLockDeadlock() { 356 writeLockA.lock(); // Establish an ordering from writeLockA -> lockB 357 lockB.lock(); 358 writeLockA.unlock(); 359 lockB.unlock(); 360 361 // lockB -> readLockA should fail. 362 lockB.lock(); 363 try { 364 readLockA.lock(); 365 fail("Expected PotentialDeadlockException"); 366 } catch (PotentialDeadlockException expected) { 367 checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB"); 368 } 369 } 370 testReadWriteLockDeadlock_transitive()371 public void testReadWriteLockDeadlock_transitive() { 372 readLockA.lock(); // Establish an ordering from readLockA -> lockB 373 lockB.lock(); 374 readLockA.unlock(); 375 lockB.unlock(); 376 377 // Establish an ordering from lockB -> lockC 378 lockB.lock(); 379 lockC.lock(); 380 lockB.unlock(); 381 lockC.unlock(); 382 383 // lockC -> writeLockA should fail. 384 lockC.lock(); 385 try { 386 writeLockA.lock(); 387 fail("Expected PotentialDeadlockException"); 388 } catch (PotentialDeadlockException expected) { 389 checkMessage(expected, "LockC -> ReadWriteA", "LockB -> LockC", "ReadWriteA -> LockB"); 390 } 391 } 392 testReadWriteLockDeadlock_treatedEquivalently()393 public void testReadWriteLockDeadlock_treatedEquivalently() { 394 readLockA.lock(); // readLockA -> writeLockB 395 writeLockB.lock(); 396 readLockA.unlock(); 397 writeLockB.unlock(); 398 399 // readLockB -> writeLockA should fail. 400 readLockB.lock(); 401 try { 402 writeLockA.lock(); 403 fail("Expected PotentialDeadlockException"); 404 } catch (PotentialDeadlockException expected) { 405 checkMessage(expected, "ReadWriteB -> ReadWriteA", "ReadWriteA -> ReadWriteB"); 406 } 407 } 408 testDifferentLockFactories()409 public void testDifferentLockFactories() { 410 CycleDetectingLockFactory otherFactory = CycleDetectingLockFactory.newInstance(Policies.WARN); 411 ReentrantLock lockD = otherFactory.newReentrantLock("LockD"); 412 413 // lockA -> lockD 414 lockA.lock(); 415 lockD.lock(); 416 lockA.unlock(); 417 lockD.unlock(); 418 419 // lockD -> lockA should fail even though lockD is from a different factory. 420 lockD.lock(); 421 try { 422 lockA.lock(); 423 fail("Expected PotentialDeadlockException"); 424 } catch (PotentialDeadlockException expected) { 425 checkMessage(expected, "LockD -> LockA", "LockA -> LockD"); 426 } 427 } 428 testDifferentLockFactories_policyExecution()429 public void testDifferentLockFactories_policyExecution() { 430 CycleDetectingLockFactory otherFactory = CycleDetectingLockFactory.newInstance(Policies.WARN); 431 ReentrantLock lockD = otherFactory.newReentrantLock("LockD"); 432 433 // lockD -> lockA 434 lockD.lock(); 435 lockA.lock(); 436 lockA.unlock(); 437 lockD.unlock(); 438 439 // lockA -> lockD should warn but otherwise succeed because lockD was 440 // created by a factory with the WARN policy. 441 lockA.lock(); 442 lockD.lock(); 443 } 444 445 testReentrantLock_tryLock()446 public void testReentrantLock_tryLock() throws Exception { 447 LockingThread thread = new LockingThread(lockA); 448 thread.start(); 449 450 thread.waitUntilHoldingLock(); 451 assertFalse(lockA.tryLock()); 452 453 thread.releaseLockAndFinish(); 454 assertTrue(lockA.tryLock()); 455 } 456 457 testReentrantWriteLock_tryLock()458 public void testReentrantWriteLock_tryLock() throws Exception { 459 LockingThread thread = new LockingThread(writeLockA); 460 thread.start(); 461 462 thread.waitUntilHoldingLock(); 463 assertFalse(writeLockA.tryLock()); 464 assertFalse(readLockA.tryLock()); 465 466 thread.releaseLockAndFinish(); 467 assertTrue(writeLockA.tryLock()); 468 assertTrue(readLockA.tryLock()); 469 } 470 471 testReentrantReadLock_tryLock()472 public void testReentrantReadLock_tryLock() throws Exception { 473 LockingThread thread = new LockingThread(readLockA); 474 thread.start(); 475 476 thread.waitUntilHoldingLock(); 477 assertFalse(writeLockA.tryLock()); 478 assertTrue(readLockA.tryLock()); 479 readLockA.unlock(); 480 481 thread.releaseLockAndFinish(); 482 assertTrue(writeLockA.tryLock()); 483 assertTrue(readLockA.tryLock()); 484 } 485 486 private static class LockingThread extends Thread { 487 final CountDownLatch locked = new CountDownLatch(1); 488 final CountDownLatch finishLatch = new CountDownLatch(1); 489 final Lock lock; 490 LockingThread(Lock lock)491 LockingThread(Lock lock) { 492 this.lock = lock; 493 } 494 495 @Override run()496 public void run() { 497 lock.lock(); 498 try { 499 locked.countDown(); 500 finishLatch.await(1, TimeUnit.MINUTES); 501 } catch (InterruptedException e) { 502 fail(e.toString()); 503 } finally { 504 lock.unlock(); 505 } 506 } 507 waitUntilHoldingLock()508 void waitUntilHoldingLock() throws InterruptedException { 509 locked.await(1, TimeUnit.MINUTES); 510 } 511 releaseLockAndFinish()512 void releaseLockAndFinish() throws InterruptedException { 513 finishLatch.countDown(); 514 this.join(10000); 515 assertFalse(this.isAlive()); 516 } 517 } 518 testReentrantReadWriteLock_implDoesNotExposeShadowedLocks()519 public void testReentrantReadWriteLock_implDoesNotExposeShadowedLocks() { 520 assertEquals( 521 "Unexpected number of public methods in ReentrantReadWriteLock. " 522 + "The correctness of CycleDetectingReentrantReadWriteLock depends on " 523 + "the fact that the shadowed ReadLock and WriteLock are never used or " 524 + "exposed by the superclass implementation. If the implementation has " 525 + "changed, the code must be re-inspected to ensure that the " 526 + "assumption is still valid.", 527 24, 528 ReentrantReadWriteLock.class.getMethods().length); 529 } 530 531 private enum MyOrder { 532 FIRST, 533 SECOND, 534 THIRD; 535 } 536 537 private enum OtherOrder { 538 FIRST, 539 SECOND, 540 THIRD; 541 } 542 543 // Given a sequence of lock acquisition descriptions 544 // (e.g. "LockA -> LockB", "LockB -> LockC", ...) 545 // Checks that the exception.getMessage() matches a regex of the form: 546 // "LockA -> LockB \b.*\b LockB -> LockC \b.*\b LockC -> LockA" checkMessage(IllegalStateException exception, String... expectedLockCycle)547 private void checkMessage(IllegalStateException exception, String... expectedLockCycle) { 548 String regex = Joiner.on("\\b.*\\b").join(expectedLockCycle); 549 assertContainsRegex(regex, exception.getMessage()); 550 } 551 552 // TODO(cpovirk): consider adding support for regex to Truth assertContainsRegex(String expectedRegex, String actual)553 private static void assertContainsRegex(String expectedRegex, String actual) { 554 Pattern pattern = Pattern.compile(expectedRegex); 555 Matcher matcher = pattern.matcher(actual); 556 if (!matcher.find()) { 557 String actualDesc = (actual == null) ? "null" : ('<' + actual + '>'); 558 fail("expected to contain regex:<" + expectedRegex + "> but was:" + actualDesc); 559 } 560 } 561 } 562