Skip to content

Instantly share code, notes, and snippets.

@franz1981
Created December 3, 2025 21:21
Show Gist options
  • Select an option

  • Save franz1981/5148c4843a384a6a6299cee0bc87e347 to your computer and use it in GitHub Desktop.

Select an option

Save franz1981/5148c4843a384a6a6299cee0bc87e347 to your computer and use it in GitHub Desktop.
private static final ScopedValue<ExecutionContext> EXECUTION_CTX = ScopedValue.newInstance();
private static final class ExecutionContext {
private final ReentrantLock lock = new ReentrantLock(true);
private final AtomicReference<CountDownLatch> suspended = new AtomicReference<>(null);
/**
* This can be called by any thread!
*/
public void resume() {
var toResume = suspended.getAndSet(null);
if (toResume != null) {
toResume.countDown();
}
}
public boolean isSuspended() {
return suspended.get() != null;
}
void suspend() {
var ctx = EXECUTION_CTX.orElseThrow(IllegalStateException::new);
if (ctx != this) {
throw new IllegalStateException("Wrong execution context");
}
assert lock.isHeldByCurrentThread();
var latch = new CountDownLatch(1);
suspended.lazySet(latch);
try {
latch.await();
} catch (Throwable _) {
}
}
private void runSerialized(Runnable task) {
lock.lock();
try {
task.run();
} finally {
lock.unlock();
}
}
}
@Test
void testSerializeExecution() throws Exception {
List<Integer> sequence = Collections.synchronizedList(new ArrayList<>());
try (var group = new VirtualMultithreadIoEventLoopGroup(1, LocalIoHandler.newFactory())) {
var eventLoopFactory = group.vThreadFactory();
var sharedExecutionCtx = new ExecutionContext();
ThreadFactory serializerFactory = task -> eventLoopFactory.newThread(() -> ScopedValue
.where(EXECUTION_CTX, sharedExecutionCtx).run(() -> sharedExecutionCtx.runSerialized(task)));
serializerFactory.newThread(() -> {
var ctx = EXECUTION_CTX.get();
Thread.startVirtualThread(() -> {
// make sure that the context is suspended BEFORE trying to resume it
while (!ctx.isSuspended()) {
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1));
}
serializerFactory.newThread(() -> {
sequence.add(3);
}).start();
// give enough time for the previous vThread to start and try to run
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
ctx.resume();
});
sequence.add(1);
ctx.suspend();
sequence.add(2);
}).start();
long now = System.currentTimeMillis();
while (true) {
assertTrue(System.currentTimeMillis() - now < 20_000);
if (sequence.size() == 3) {
assertEquals(List.of(1, 2, 3), sequence);
break;
}
Thread.sleep(1);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment