Skip to content

Commit

Permalink
Use ReentrantReadWriteLock in the GlobalContextStore
Browse files Browse the repository at this point in the history
  • Loading branch information
kefirfromperm committed Jul 19, 2024
1 parent df70c6c commit 5c146e7
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 32 deletions.
63 changes: 39 additions & 24 deletions src/main/java/mina/context/GlobalContextStore.java
Original file line number Diff line number Diff line change
@@ -1,58 +1,73 @@
package mina.context;

import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class GlobalContextStore implements ContextStore {
private final AtomicReference<MinaContext> globalContext = new AtomicReference<>();
private final AtomicReference<WeakReference<Thread>> ownerThread = new AtomicReference<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private MinaContext globalContext = null;
private WeakReference<Thread> ownerThread = null;

@Override
public synchronized MinaContext createOrGetContext() {
assertParallelAccessToGlobalContext();
return globalContext.updateAndGet(context -> context == null ? new MinaContext() : context);
public MinaContext createOrGetContext() {
lock.writeLock().lock();
try {
assertParallelAccessToGlobalContext();
if (globalContext == null) {
globalContext = new MinaContext();
ownerThread = new WeakReference<>(Thread.currentThread());
}
return globalContext;
} finally {
lock.writeLock().unlock();
}
}

@Override
public MinaContext getContext() {
return globalContext.get();
lock.readLock().lock();
try {
return globalContext;
} finally {
lock.readLock().unlock();
}
}

@Override
public synchronized void removeContext() {
assertParallelAccessToGlobalContext();
globalContext.set(null);
ownerThread.set(null);
public void removeContext() {
lock.writeLock().lock();
try {
assertParallelAccessToGlobalContext();
globalContext = null;
ownerThread = null;
} finally {
lock.writeLock().unlock();
}
}

@Override
public void close() {
removeContext();
}

private synchronized void assertParallelAccessToGlobalContext() {
if (globalContext.get() == null) {
private void assertParallelAccessToGlobalContext() {
if (globalContext == null) {
return;
}

Thread currentThread = Thread.currentThread();

Thread ownerThread = this.ownerThread.updateAndGet((weak) -> {
Thread thread = weak != null ? weak.get() : null;
if (thread == null || !thread.isAlive()) {
return new WeakReference<>(currentThread);
} else {
return weak;
}
}).get();
Thread ownerThread = this.ownerThread.get();
if (ownerThread == null || !ownerThread.isAlive()) {
return;
}

Thread currentThread = Thread.currentThread();
if (currentThread == ownerThread) {
return;
}

throw new IllegalStateException(
"Mina is configured to use GLOBAL context in a single thread tests but multi access to the context detected. " +
"Current thread is [" + currentThread.getName() + "] but the owner thread is [" + (ownerThread != null ? ownerThread.getName() : null) + "]"
"Current thread is [" + currentThread.getName() + "] but the owner thread is [" + ownerThread.getName() + "]"
);
}

Expand Down
25 changes: 17 additions & 8 deletions src/test/java/mina/core/MinaTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import org.slf4j.MDC;

import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

import static mina.core.Mina.*;
Expand All @@ -21,6 +23,7 @@ public class MinaTest {
@AfterEach
public void clean() {
Mina.clean();
log.info("Cleaned up");
}

@Test
Expand Down Expand Up @@ -280,7 +283,7 @@ public void testParallel() {

AtomicInteger count = new AtomicInteger();
on(Simple.class).checkCanonical(
(index, arguments, throwable) -> assertEquals(count.incrementAndGet(), index)
(index, arguments, throwable) -> count.incrementAndGet()
);

new Simple().doParallel();
Expand All @@ -291,19 +294,25 @@ public void testParallel() {
}

@Test
public void testParallelTests() {
public void testParallelTests() throws InterruptedException {
// When using global context
Mina.useGlobalContext();

assertDoesNotThrow(() -> on().check());

// We try to run Mina in parallel tests
assertThrows(IllegalStateException.class, () ->
Arrays.asList(1, 2, 3, 4, 5).parallelStream().forEach(value -> {
log.info("test {}", value);
on().check();
})
);
try {
Executors.newSingleThreadExecutor().submit(() -> {
log.info("a parallel test");
on().check();
}).get();
fail();
} catch (ExecutionException interruptedException) {
Throwable cause = interruptedException.getCause();
assertNotNull(cause);
assertInstanceOf(IllegalStateException.class, cause);
assertTrue(cause.getMessage().startsWith("Mina is configured to use GLOBAL context"));
}
}

@Test
Expand Down

0 comments on commit 5c146e7

Please sign in to comment.