Skip to content

Commit a7b42c4

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 a7b42c4

2 files changed

Lines changed: 11 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: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,14 @@ public void doClose()
881881
}
882882
finally
883883
{
884+
// release memory-mapped resources only after the agent has stopped, not in onClose,
885+
// so a self-terminated worker does not unmap buffers the engine or peers still reference
886+
targetsByIndex.forEach((k, v) -> quietClose(v));
887+
quietClose(streamsLayout);
888+
quietClose(bufferPoolLayout);
889+
debitorsByIndex.forEach((k, v) -> quietClose(v));
890+
quietClose(creditor);
891+
quietClose(eventWriter);
884892
thread = null;
885893
}
886894
}
@@ -1058,14 +1066,6 @@ public void onClose()
10581066
}
10591067

10601068
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);
10691069

10701070
if (acquiredBuffers != 0 || acquiredCreditors != 0 || acquiredDebitors != 0L)
10711071
{

0 commit comments

Comments
 (0)