use of com.google.inject.internal.CycleDetectingLock.CycleDetectingLockFactory.ReentrantCycleDetectingLock in project guice by google.
the class CycleDetectingLockTest method testSingletonThreadsRuntimeCircularDependency.
/**
* Verifies that graph of threads' dependencies is not static and is calculated in runtime using
* information about specific locks.
*
* <pre>
* T1: Waits on S1
* T2: Locks B, sends S1, waits on S2
* T1: Locks A, start locking B, sends S2, waits on S3
* T2: Unlocks B, start locking A, sends S3, finishes locking A, unlocks A
* T1: Finishes locking B, unlocks B, unlocks A
* </pre>
*
* <p>This should succeed, even though T1 was locked on T2 and T2 is locked on T1 when T2 locks A.
* Incorrect implementation detects a cycle waiting on S3.
*/
public void testSingletonThreadsRuntimeCircularDependency() throws Exception {
final CyclicBarrier signal1 = new CyclicBarrier(2);
final CyclicBarrier signal2 = new CyclicBarrier(2);
final CyclicBarrier signal3 = new CyclicBarrier(2);
final CycleDetectingLockFactory<String> lockFactory = new CycleDetectingLockFactory<String>();
final CycleDetectingLock<String> lockA = new ReentrantCycleDetectingLock<String>(lockFactory, "A", new ReentrantLock() {
@Override
public void lock() {
if (Thread.currentThread().getName().equals("T2")) {
try {
signal3.await(DEADLOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
assertEquals("T1", Thread.currentThread().getName());
}
super.lock();
}
});
final CycleDetectingLock<String> lockB = new ReentrantCycleDetectingLock<String>(lockFactory, "B", new ReentrantLock() {
@Override
public void lock() {
if (Thread.currentThread().getName().equals("T1")) {
try {
signal2.await(DEADLOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
signal3.await(DEADLOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
assertEquals("T2", Thread.currentThread().getName());
}
super.lock();
}
});
Future<Void> firstThreadResult = Executors.newSingleThreadExecutor().submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
Thread.currentThread().setName("T1");
signal1.await(DEADLOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue(lockA.lockOrDetectPotentialLocksCycle().isEmpty());
assertTrue(lockB.lockOrDetectPotentialLocksCycle().isEmpty());
lockB.unlock();
lockA.unlock();
return null;
}
});
Future<Void> secondThreadResult = Executors.newSingleThreadExecutor().submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
Thread.currentThread().setName("T2");
assertTrue(lockB.lockOrDetectPotentialLocksCycle().isEmpty());
signal1.await(DEADLOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
signal2.await(DEADLOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
lockB.unlock();
assertTrue(lockA.lockOrDetectPotentialLocksCycle().isEmpty());
lockA.unlock();
return null;
}
});
firstThreadResult.get(DEADLOCK_TIMEOUT_SECONDS * 3, TimeUnit.SECONDS);
secondThreadResult.get(DEADLOCK_TIMEOUT_SECONDS * 3, TimeUnit.SECONDS);
}
Aggregations