Skip to content

Commit 6413eee

Browse files
jfallowsclaude
andcommitted
fix(engine): release worker buffers in coordinated close to avoid JVM crash on binding fault
A hot-path binding exception is wrapped by EngineWorker.doWork as AgentTerminationException, terminating the worker agent. Agrona then runs onClose on the agent thread, which released the worker's memory-mapped buffers in advance. The engine-coordinated Engine.close() -> drain() then read that worker's unmapped streams ring buffer via Unsafe, causing a SIGSEGV / JVM abort. Defer all worker memory-mapped resource releases (targets, streamsLayout, bufferPoolLayout, debitors, creditor, eventWriter) from onClose to doClose's finally, which runs after the agent has stopped, so a self-terminated worker no longer unmaps buffers the engine or peer workers still reference. Engine.close also skips draining workers whose agent already terminated, which would otherwise spin until the drain timeout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 544f3df commit 6413eee

2 files changed

Lines changed: 9 additions & 9 deletions

File tree

runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/Engine.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,9 @@ public void close() throws Exception
332332

333333
if (config.drainOnClose())
334334
{
335-
workers.forEach(EngineWorker::drain);
335+
workers.stream()
336+
.filter(worker -> !worker.runner().isClosed())
337+
.forEach(EngineWorker::drain);
336338
}
337339

338340
final List<Throwable> errors = new ArrayList<>();

runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/internal/registry/EngineWorker.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,12 @@ public void doClose()
881881
}
882882
finally
883883
{
884+
targetsByIndex.forEach((k, v) -> quietClose(v));
885+
quietClose(streamsLayout);
886+
quietClose(bufferPoolLayout);
887+
debitorsByIndex.forEach((k, v) -> quietClose(v));
888+
quietClose(creditor);
889+
quietClose(eventWriter);
884890
thread = null;
885891
}
886892
}
@@ -1058,14 +1064,6 @@ public void onClose()
10581064
}
10591065

10601066
targetsByIndex.forEach((k, v) -> v.detach());
1061-
targetsByIndex.forEach((k, v) -> quietClose(v));
1062-
1063-
quietClose(streamsLayout);
1064-
quietClose(bufferPoolLayout);
1065-
1066-
debitorsByIndex.forEach((k, v) -> quietClose(v));
1067-
quietClose(creditor);
1068-
quietClose(eventWriter);
10691067

10701068
if (acquiredBuffers != 0 || acquiredCreditors != 0 || acquiredDebitors != 0L)
10711069
{

0 commit comments

Comments
 (0)