aboutsummaryrefslogtreecommitdiffstats
path: root/zkfacade/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'zkfacade/src/test')
-rw-r--r--zkfacade/src/test/java/com/yahoo/vespa/curator/stats/LockTest.java75
1 files changed, 70 insertions, 5 deletions
diff --git a/zkfacade/src/test/java/com/yahoo/vespa/curator/stats/LockTest.java b/zkfacade/src/test/java/com/yahoo/vespa/curator/stats/LockTest.java
index f440c2cfad8..c28691fe655 100644
--- a/zkfacade/src/test/java/com/yahoo/vespa/curator/stats/LockTest.java
+++ b/zkfacade/src/test/java/com/yahoo/vespa/curator/stats/LockTest.java
@@ -1,6 +1,7 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.curator.stats;
+import com.google.common.util.concurrent.UncheckedTimeoutException;
import com.yahoo.vespa.curator.Lock;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.junit.Before;
@@ -9,6 +10,9 @@ import org.junit.Test;
import java.time.Duration;
import java.util.List;
import java.util.Optional;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -27,7 +31,7 @@ public class LockTest {
private final InterProcessLock mutex = mock(InterProcessLock.class);
private final String lockPath = "/lock/path";
private final String lock2Path = "/lock2/path";
- private final Duration acquireTimeout = Duration.ofSeconds(10);
+ private static final Duration acquireTimeout = Duration.ofMinutes(10);
private final Lock lock = new Lock(lockPath, mutex);
private final Lock lock2 = new Lock(lock2Path, mutex);
@@ -176,9 +180,6 @@ public class LockTest {
public void nestedLocks() throws Exception {
when(mutex.acquire(anyLong(), any())).thenReturn(true);
- String lockPath2 = "/lock/path/2";
- Lock lock2 = new Lock(lockPath2, mutex);
-
lock.acquire(acquireTimeout);
lock2.acquire(acquireTimeout);
@@ -188,10 +189,74 @@ public class LockTest {
assertEquals(2, lockAttempts.size());
assertEquals(lockPath, lockAttempts.get(0).getLockPath());
assertEquals(LockAttempt.LockState.ACQUIRED, lockAttempts.get(0).getLockState());
- assertEquals(lockPath2, lockAttempts.get(1).getLockPath());
+ assertEquals(lock2Path, lockAttempts.get(1).getLockPath());
assertEquals(LockAttempt.LockState.ACQUIRED, lockAttempts.get(1).getLockState());
lock.close();
lock.close();
}
+
+ @Test
+ public void deadlock() throws Exception {
+ var lockPath1 = "/lock/path/1";
+ var lockPath2 = "/lock/path/2";
+
+ var lock1 = new Lock(lockPath1, new InterProcessMutexMock());
+ var lock2 = new Lock(lockPath2, new InterProcessMutexMock());
+
+ lock2.acquire(acquireTimeout);
+
+ Thread thread = Executors.defaultThreadFactory().newThread(() -> threadMain(lock1, lock2));
+ thread.setName("LockTest-async-thread");
+ thread.start();
+
+ LockStats globalStats = LockStats.getGlobal();
+ ThreadLockStats asyncThreadStats = globalStats.getForThread(thread);
+ while (true) {
+ Optional<LockAttempt> bottomMostOngoingLockAttempt = asyncThreadStats.getBottomMostOngoingLockAttempt();
+ if (bottomMostOngoingLockAttempt.isPresent() &&
+ bottomMostOngoingLockAttempt.get().getLockPath().equals(lockPath2)) {
+ break;
+ }
+
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException e) { }
+ }
+
+ try {
+ lock1.acquire(acquireTimeout);
+ fail();
+ } catch (UncheckedTimeoutException e) {
+ assertEquals(
+ "Unexpected timeout exception message: " + e.getMessage(),
+ "Deadlock detected: Thread main, " +
+ "trying to acquire lock /lock/path/1 held by thread LockTest-async-thread, " +
+ "trying to acquire lock /lock/path/2 held by thread main",
+ e.getMessage());
+ }
+
+ // Unlock, which unblocks thread
+ lock2.close();
+ thread.join();
+ }
+
+ private static void threadMain(Lock lock1, Lock lock2) {
+ lock1.acquire(acquireTimeout);
+
+ // This will block
+ lock2.acquire(acquireTimeout);
+
+ lock2.close();
+
+ lock1.close();
+ }
+
+ private static class InterProcessMutexMock implements InterProcessLock {
+ private final ReentrantLock lock = new ReentrantLock();
+ @Override public void acquire() throws Exception { lock.lock(); }
+ @Override public boolean acquire(long time, TimeUnit unit) throws Exception { acquire(); return true; }
+ @Override public void release() throws Exception { lock.unlock(); }
+ @Override public boolean isAcquiredInThisProcess() { return lock.isLocked(); }
+ }
}