diff options
Diffstat (limited to 'guava-tests/test/com/google/common/util/concurrent/CycleDetectingLockFactoryTest.java')
-rw-r--r-- | guava-tests/test/com/google/common/util/concurrent/CycleDetectingLockFactoryTest.java | 581 |
1 files changed, 0 insertions, 581 deletions
diff --git a/guava-tests/test/com/google/common/util/concurrent/CycleDetectingLockFactoryTest.java b/guava-tests/test/com/google/common/util/concurrent/CycleDetectingLockFactoryTest.java deleted file mode 100644 index b39601f..0000000 --- a/guava-tests/test/com/google/common/util/concurrent/CycleDetectingLockFactoryTest.java +++ /dev/null @@ -1,581 +0,0 @@ -/* - * Copyright (C) 2011 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.util.concurrent; - -import com.google.common.base.Joiner; -import com.google.common.util.concurrent.CycleDetectingLockFactory.OrderedLockGraphNodesCreator; -import com.google.common.util.concurrent.CycleDetectingLockFactory.Policies; -import com.google.common.util.concurrent.CycleDetectingLockFactory.Policy; -import com.google.common.util.concurrent.CycleDetectingLockFactory.PotentialDeadlockException; - -import junit.framework.TestCase; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Unittests for {@link CycleDetectingLockFactory}. - * - * @author Darick Tong - */ -public class CycleDetectingLockFactoryTest extends TestCase { - - private ReentrantLock lockA; - private ReentrantLock lockB; - private ReentrantLock lockC; - private ReentrantReadWriteLock.ReadLock readLockA; - private ReentrantReadWriteLock.ReadLock readLockB; - private ReentrantReadWriteLock.ReadLock readLockC; - private ReentrantReadWriteLock.WriteLock writeLockA; - private ReentrantReadWriteLock.WriteLock writeLockB; - private ReentrantReadWriteLock.WriteLock writeLockC; - private ReentrantLock lock1; - private ReentrantLock lock2; - private ReentrantLock lock3; - private ReentrantLock lock01; - private ReentrantLock lock02; - private ReentrantLock lock03; - - @Override - protected void setUp() throws Exception { - super.setUp(); - CycleDetectingLockFactory factory = - CycleDetectingLockFactory.newInstance(Policies.THROW); - lockA = factory.newReentrantLock("LockA"); - lockB = factory.newReentrantLock("LockB"); - lockC = factory.newReentrantLock("LockC"); - ReentrantReadWriteLock readWriteLockA = - factory.newReentrantReadWriteLock("ReadWriteA"); - ReentrantReadWriteLock readWriteLockB = - factory.newReentrantReadWriteLock("ReadWriteB"); - ReentrantReadWriteLock readWriteLockC = - factory.newReentrantReadWriteLock("ReadWriteC"); - readLockA = readWriteLockA.readLock(); - readLockB = readWriteLockB.readLock(); - readLockC = readWriteLockC.readLock(); - writeLockA = readWriteLockA.writeLock(); - writeLockB = readWriteLockB.writeLock(); - writeLockC = readWriteLockC.writeLock(); - - CycleDetectingLockFactory.WithExplicitOrdering<MyOrder> factory2 = - newInstanceWithExplicitOrdering(MyOrder.class, Policies.THROW); - lock1 = factory2.newReentrantLock(MyOrder.FIRST); - lock2 = factory2.newReentrantLock(MyOrder.SECOND); - lock3 = factory2.newReentrantLock(MyOrder.THIRD); - - CycleDetectingLockFactory.WithExplicitOrdering<OtherOrder> factory3 = - newInstanceWithExplicitOrdering(OtherOrder.class, Policies.THROW); - lock01 = factory3.newReentrantLock(OtherOrder.FIRST); - lock02 = factory3.newReentrantLock(OtherOrder.SECOND); - lock03 = factory3.newReentrantLock(OtherOrder.THIRD); - } - - // In the unittest, create each ordered factory with its own set of lock - // graph nodes (as opposed to using the static per-Enum map) to avoid - // conflicts across different test runs. - private <E extends Enum<E>> CycleDetectingLockFactory.WithExplicitOrdering<E> - newInstanceWithExplicitOrdering(Class<E> enumClass, Policy policy) { - OrderedLockGraphNodesCreator nodeCreator = - new OrderedLockGraphNodesCreator(); - return new CycleDetectingLockFactory.WithExplicitOrdering<E>( - policy, nodeCreator.createNodesFor(enumClass)); - } - - public void testDeadlock_twoLocks() { - // Establish an acquisition order of lockA -> lockB. - lockA.lock(); - lockB.lock(); - lockA.unlock(); - lockB.unlock(); - - // The opposite order should fail (Policies.THROW). - PotentialDeadlockException firstException = null; - lockB.lock(); - try { - lockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "LockB -> LockA", "LockA -> LockB"); - firstException = expected; - } - - // Second time should also fail, with a cached causal chain. - try { - lockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "LockB -> LockA", "LockA -> LockB"); - // The causal chain should be cached. - assertSame(firstException.getCause(), expected.getCause()); - } - - // lockA should work after lockB is released. - lockB.unlock(); - lockA.lock(); - } - - // Tests transitive deadlock detection. - public void testDeadlock_threeLocks() { - // Establish an ordering from lockA -> lockB. - lockA.lock(); - lockB.lock(); - lockB.unlock(); - lockA.unlock(); - - // Establish an ordering from lockB -> lockC. - lockB.lock(); - lockC.lock(); - lockB.unlock(); - - // lockC -> lockA should fail. - try { - lockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage( - expected, "LockC -> LockA", "LockB -> LockC", "LockA -> LockB"); - } - } - - public void testReentrancy_noDeadlock() { - lockA.lock(); - lockB.lock(); - lockA.lock(); // Should not assert on lockB -> reentrant(lockA) - } - - public void testExplicitOrdering_noViolations() { - lock1.lock(); - lock3.lock(); - lock3.unlock(); - lock2.lock(); - lock3.lock(); - } - - public void testExplicitOrdering_violations() { - lock3.lock(); - try { - lock2.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "MyOrder.THIRD -> MyOrder.SECOND"); - } - - try { - lock1.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "MyOrder.THIRD -> MyOrder.FIRST"); - } - - lock3.unlock(); - lock2.lock(); - - try { - lock1.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "MyOrder.SECOND -> MyOrder.FIRST"); - } - } - - public void testDifferentOrderings_noViolations() { - lock3.lock(); // MyOrder, ordinal() == 3 - lock01.lock(); // OtherOrder, ordinal() == 1 - } - - public void testExplicitOrderings_generalCycleDetection() { - lock3.lock(); // MyOrder, ordinal() == 3 - lock01.lock(); // OtherOrder, ordinal() == 1 - - lock3.unlock(); - try { - lock3.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage( - expected, - "OtherOrder.FIRST -> MyOrder.THIRD", - "MyOrder.THIRD -> OtherOrder.FIRST"); - } - - lockA.lock(); - lock01.unlock(); - lockB.lock(); - lockA.unlock(); - - try { - lock01.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage( - expected, - "LockB -> OtherOrder.FIRST", - "LockA -> LockB", - "OtherOrder.FIRST -> LockA"); - } - } - - public void testExplicitOrdering_cycleWithUnorderedLock() { - Lock myLock = CycleDetectingLockFactory.newInstance(Policies.THROW) - .newReentrantLock("MyLock"); - lock03.lock(); - myLock.lock(); - lock03.unlock(); - - try { - lock01.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage( - expected, - "MyLock -> OtherOrder.FIRST", - "OtherOrder.THIRD -> MyLock", - "OtherOrder.FIRST -> OtherOrder.THIRD"); - } - } - - public void testExplicitOrdering_reentrantAcquisition() { - CycleDetectingLockFactory.WithExplicitOrdering<OtherOrder> factory = - newInstanceWithExplicitOrdering(OtherOrder.class, Policies.THROW); - Lock lockA = factory.newReentrantReadWriteLock(OtherOrder.FIRST).readLock(); - Lock lockB = factory.newReentrantLock(OtherOrder.SECOND); - - lockA.lock(); - lockA.lock(); - lockB.lock(); - lockB.lock(); - lockA.unlock(); - lockA.unlock(); - lockB.unlock(); - lockB.unlock(); - } - - public void testExplicitOrdering_acquiringMultipleLocksWithSameRank() { - CycleDetectingLockFactory.WithExplicitOrdering<OtherOrder> factory = - newInstanceWithExplicitOrdering(OtherOrder.class, Policies.THROW); - Lock lockA = factory.newReentrantLock(OtherOrder.FIRST); - Lock lockB = factory.newReentrantReadWriteLock(OtherOrder.FIRST).readLock(); - - lockA.lock(); - try { - lockB.lock(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - - lockA.unlock(); - lockB.lock(); - } - - public void testReadLock_deadlock() { - readLockA.lock(); // Establish an ordering from readLockA -> lockB. - lockB.lock(); - lockB.unlock(); - readLockA.unlock(); - - lockB.lock(); - try { - readLockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB"); - } - } - - public void testReadLock_transitive() { - readLockA.lock(); // Establish an ordering from readLockA -> lockB. - lockB.lock(); - lockB.unlock(); - readLockA.unlock(); - - // Establish an ordering from lockB -> readLockC. - lockB.lock(); - readLockC.lock(); - lockB.unlock(); - readLockC.unlock(); - - // readLockC -> readLockA - readLockC.lock(); - try { - readLockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage( - expected, - "ReadWriteC -> ReadWriteA", - "LockB -> ReadWriteC", - "ReadWriteA -> LockB"); - } - } - - public void testWriteLock_threeLockDeadLock() { - // Establish an ordering from writeLockA -> writeLockB. - writeLockA.lock(); - writeLockB.lock(); - writeLockB.unlock(); - writeLockA.unlock(); - - // Establish an ordering from writeLockB -> writeLockC. - writeLockB.lock(); - writeLockC.lock(); - writeLockB.unlock(); - - // writeLockC -> writeLockA should fail. - try { - writeLockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage( - expected, - "ReadWriteC -> ReadWriteA", - "ReadWriteB -> ReadWriteC", - "ReadWriteA -> ReadWriteB"); - } - } - - public void testWriteToReadLockDowngrading() { - writeLockA.lock(); // writeLockA downgrades to readLockA - readLockA.lock(); - writeLockA.unlock(); - - lockB.lock(); // readLockA -> lockB - readLockA.unlock(); - - // lockB -> writeLockA should fail - try { - writeLockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage( - expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB"); - } - } - - public void testReadWriteLockDeadlock() { - writeLockA.lock(); // Establish an ordering from writeLockA -> lockB - lockB.lock(); - writeLockA.unlock(); - lockB.unlock(); - - // lockB -> readLockA should fail. - lockB.lock(); - try { - readLockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage( - expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB"); - } - } - - public void testReadWriteLockDeadlock_transitive() { - readLockA.lock(); // Establish an ordering from readLockA -> lockB - lockB.lock(); - readLockA.unlock(); - lockB.unlock(); - - // Establish an ordering from lockB -> lockC - lockB.lock(); - lockC.lock(); - lockB.unlock(); - lockC.unlock(); - - // lockC -> writeLockA should fail. - lockC.lock(); - try { - writeLockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage( - expected, - "LockC -> ReadWriteA", - "LockB -> LockC", - "ReadWriteA -> LockB"); - } - } - - public void testReadWriteLockDeadlock_treatedEquivalently() { - readLockA.lock(); // readLockA -> writeLockB - writeLockB.lock(); - readLockA.unlock(); - writeLockB.unlock(); - - // readLockB -> writeLockA should fail. - readLockB.lock(); - try { - writeLockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage( - expected, "ReadWriteB -> ReadWriteA", "ReadWriteA -> ReadWriteB"); - } - } - - public void testDifferentLockFactories() { - CycleDetectingLockFactory otherFactory = - CycleDetectingLockFactory.newInstance(Policies.WARN); - ReentrantLock lockD = otherFactory.newReentrantLock("LockD"); - - // lockA -> lockD - lockA.lock(); - lockD.lock(); - lockA.unlock(); - lockD.unlock(); - - // lockD -> lockA should fail even though lockD is from a different factory. - lockD.lock(); - try { - lockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "LockD -> LockA", "LockA -> LockD"); - } - } - - public void testDifferentLockFactories_policyExecution() { - CycleDetectingLockFactory otherFactory = - CycleDetectingLockFactory.newInstance(Policies.WARN); - ReentrantLock lockD = otherFactory.newReentrantLock("LockD"); - - // lockD -> lockA - lockD.lock(); - lockA.lock(); - lockA.unlock(); - lockD.unlock(); - - // lockA -> lockD should warn but otherwise succeed because lockD was - // created by a factory with the WARN policy. - lockA.lock(); - lockD.lock(); - } - - public void testReentrantLock_tryLock() throws Exception { - LockingThread thread = new LockingThread(lockA); - thread.start(); - - thread.waitUntilHoldingLock(); - assertFalse(lockA.tryLock()); - - thread.releaseLockAndFinish(); - assertTrue(lockA.tryLock()); - } - - public void testReentrantWriteLock_tryLock() throws Exception { - LockingThread thread = new LockingThread(writeLockA); - thread.start(); - - thread.waitUntilHoldingLock(); - assertFalse(writeLockA.tryLock()); - assertFalse(readLockA.tryLock()); - - thread.releaseLockAndFinish(); - assertTrue(writeLockA.tryLock()); - assertTrue(readLockA.tryLock()); - } - - public void testReentrantReadLock_tryLock() throws Exception { - LockingThread thread = new LockingThread(readLockA); - thread.start(); - - thread.waitUntilHoldingLock(); - assertFalse(writeLockA.tryLock()); - assertTrue(readLockA.tryLock()); - readLockA.unlock(); - - thread.releaseLockAndFinish(); - assertTrue(writeLockA.tryLock()); - assertTrue(readLockA.tryLock()); - } - - private static class LockingThread extends Thread { - final CountDownLatch locked = new CountDownLatch(1); - final CountDownLatch finishLatch = new CountDownLatch(1); - final Lock lock; - - LockingThread(Lock lock) { - this.lock = lock; - } - - @Override - public void run() { - lock.lock(); - try { - locked.countDown(); - finishLatch.await(1, TimeUnit.MINUTES); - } catch (InterruptedException e) { - fail(e.toString()); - } finally { - lock.unlock(); - } - } - - void waitUntilHoldingLock() throws InterruptedException { - locked.await(1, TimeUnit.MINUTES); - } - - void releaseLockAndFinish() throws InterruptedException { - finishLatch.countDown(); - this.join(10000); - assertFalse(this.isAlive()); - } - } - - public void testReentrantReadWriteLock_implDoesNotExposeShadowedLocks() { - assertEquals( - "Unexpected number of public methods in ReentrantReadWriteLock. " + - "The correctness of CycleDetectingReentrantReadWriteLock depends on " + - "the fact that the shadowed ReadLock and WriteLock are never used or " + - "exposed by the superclass implementation. If the implementation has " + - "changed, the code must be re-inspected to ensure that the " + - "assumption is still valid.", - 24, ReentrantReadWriteLock.class.getMethods().length); - } - - private enum MyOrder { - FIRST, SECOND, THIRD; - } - - private enum OtherOrder { - FIRST, SECOND, THIRD; - } - - // Given a sequence of lock acquisition descriptions - // (e.g. "LockA -> LockB", "LockB -> LockC", ...) - // Checks that the exception.getMessage() matches a regex of the form: - // "LockA -> LockB \b.*\b LockB -> LockC \b.*\b LockC -> LockA" - private void checkMessage( - IllegalStateException exception, String... expectedLockCycle) { - String regex = Joiner.on("\\b.*\\b").join(expectedLockCycle); - assertContainsRegex(regex, exception.getMessage()); - } - - // TODO(cpovirk): consider adding support for regex to Truth - private static void assertContainsRegex(String expectedRegex, String actual) { - Pattern pattern = Pattern.compile(expectedRegex); - Matcher matcher = pattern.matcher(actual); - if (!matcher.find()) { - String actualDesc = (actual == null) ? "null" : ('<' + actual + '>'); - fail("expected to contain regex:<" + expectedRegex + "> but was:" - + actualDesc); - } - } -} |