From 6ff7b14a24c45559cf5356deecffc3c18b84dd54 Mon Sep 17 00:00:00 2001 From: stubbsta Date: Sun, 12 Jul 2026 18:07:55 +0200 Subject: [PATCH] Wait for graceful Anvil exit before escalating to KILL stopAnvil gave Anvil a single 200ms window after TERM before sending KILL, risking a truncated state dump during regeneration. It also checked liveness with kill -0, which succeeds for an exited-but-unreaped zombie child, so the KILL escalation fired even after clean exits. Poll osproc running() (which reaps the child) for up to 10s instead. Co-Authored-By: Claude Fable 5 --- tests/waku_rln_relay/utils_onchain.nim | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/waku_rln_relay/utils_onchain.nim b/tests/waku_rln_relay/utils_onchain.nim index 9da077933..ffbbf90a2 100644 --- a/tests/waku_rln_relay/utils_onchain.nim +++ b/tests/waku_rln_relay/utils_onchain.nim @@ -633,10 +633,17 @@ proc stopAnvil*(runAnvil: Process) {.used.} = try: when not defined(windows): discard execCmdEx(fmt"kill -TERM {anvilPID}") - # Give Anvil time to dump state on graceful shutdown before escalating to KILL. - sleep(200) - let checkResult = execCmdEx(fmt"kill -0 {anvilPID} 2>/dev/null") - if checkResult.exitCode == 0: + # Give Anvil time to dump state on graceful shutdown before escalating to + # KILL; killing too early truncates the dump and corrupts the state file. + # Poll via osproc `running` (which reaps the child) rather than `kill -0`, + # since the latter also succeeds for an exited-but-unreaped zombie. + const gracefulExitTimeoutMs = 10_000 + const pollIntervalMs = 100 + var elapsed = 0 + while runAnvil.running and elapsed < gracefulExitTimeoutMs: + sleep(pollIntervalMs) + elapsed += pollIntervalMs + if runAnvil.running: warn "Anvil process still running after TERM signal, sending KILL", anvilPID = anvilPID discard execCmdEx(fmt"kill -9 {anvilPID}")