Skip to content

Commit

Permalink
ArC: fix deadlock when calling guarded methods on the same thread
Browse files Browse the repository at this point in the history
  • Loading branch information
mkouba committed Aug 1, 2023
1 parent 773b8c1 commit 2175a98
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import java.lang.annotation.Annotation;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import jakarta.annotation.Priority;
Expand All @@ -21,7 +20,7 @@
@Priority(PLATFORM_BEFORE)
public class LockInterceptor {

private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

@AroundInvoke
Object lock(InvocationContext ctx) throws Exception {
Expand All @@ -39,21 +38,34 @@ Object lock(InvocationContext ctx) throws Exception {

private Object writeLock(Lock lock, InvocationContext ctx) throws Exception {
boolean locked = false;
int readHoldCount = rwl.getReadHoldCount();
if (readHoldCount > 0) {
// We must release all read locks hold by the current thread before acquiring write lock
for (int i = 0; i < readHoldCount; i++) {
rwl.readLock().unlock();
}
}
long time = lock.time();
try {
if (time > 0) {
locked = readWriteLock.writeLock().tryLock(time, lock.unit());
locked = rwl.writeLock().tryLock(time, lock.unit());
if (!locked) {
throw new LockException("Write lock not acquired in " + lock.unit().toMillis(time) + " ms");
}
} else {
readWriteLock.writeLock().lock();
rwl.writeLock().lock();
locked = true;
}
return ctx.proceed();
} finally {
if (readHoldCount > 0) {
// Re-aqcquire the read locks
for (int i = 0; i < readHoldCount; i++) {
rwl.readLock().lock();
}
}
if (locked) {
readWriteLock.writeLock().unlock();
rwl.writeLock().unlock();
}
}
}
Expand All @@ -63,18 +75,18 @@ private Object readLock(Lock lock, InvocationContext ctx) throws Exception {
long time = lock.time();
try {
if (time > 0) {
locked = readWriteLock.readLock().tryLock(time, lock.unit());
locked = rwl.readLock().tryLock(time, lock.unit());
if (!locked) {
throw new LockException("Read lock not acquired in " + lock.unit().toMillis(time) + " ms");
}
} else {
readWriteLock.readLock().lock();
rwl.readLock().lock();
locked = true;
}
return ctx.proceed();
} finally {
if (locked) {
readWriteLock.readLock().unlock();
rwl.readLock().unlock();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package io.quarkus.arc.test.lock;

import static org.junit.jupiter.api.Assertions.assertTrue;

import jakarta.enterprise.context.ApplicationScoped;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.Arc;
import io.quarkus.arc.Lock;
import io.quarkus.arc.Lock.Type;
import io.quarkus.arc.impl.LockInterceptor;
import io.quarkus.arc.test.ArcTestContainer;

public class LockInterceptorDeadlockTest {

@RegisterExtension
public ArcTestContainer container = new ArcTestContainer(SimpleApplicationScopedBean.class, Lock.class,
LockInterceptor.class);

@Test
public void testApplicationScopedBean() throws Exception {
SimpleApplicationScopedBean bean = Arc.container().instance(SimpleApplicationScopedBean.class).get();
assertTrue(bean.read());
assertTrue(bean.nestedRead());
assertTrue(bean.nestedWrite());
}

@ApplicationScoped
static class SimpleApplicationScopedBean {

@Lock(Type.READ)
boolean read() {
return write();
}

@Lock(Type.READ)
boolean nestedRead() {
return read();
}

@Lock(Type.WRITE)
boolean write() {
return true;
}

@Lock(Type.WRITE)
boolean nestedWrite() {
return nestedRead();
}
}
}

0 comments on commit 2175a98

Please sign in to comment.