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