From 49d320a9725f1c076c478a80b0575efcb4c89bc6 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Wed, 8 Jul 2026 10:25:28 -0400 Subject: [PATCH] test: stabilize perf-316 lock-holder holdMs race under load Worker A (lock holder) used holdMs=1000 as a safety cap on its Atomics.wait for the writer's contention signal. Under container load (full suite, node24) Worker B's spawn + require('state.cjs') + stub installation can exceed 1s, so A's wait timed out and removed the lock before B ever contended. B's first atomic-create then succeeded (lockAttempts:1), failing the retry-path witness and red-flagging the gsd-test gate on otherwise-green branches. The handshake is the real release trigger; holdMs is only a safety cap for a dead/hung writer, so it must be large enough to never elapse during B's spawn+init. Bump to 30s (bounded; afterEach terminate()s A on the normal path, so no added latency) and widen the per-test timeout to 15s for spawn headroom under heavy parallel load. --- tests/perf-316-state-lock-buffer-alloc.test.cjs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/perf-316-state-lock-buffer-alloc.test.cjs b/tests/perf-316-state-lock-buffer-alloc.test.cjs index 4e0e04328..2fab704fd 100644 --- a/tests/perf-316-state-lock-buffer-alloc.test.cjs +++ b/tests/perf-316-state-lock-buffer-alloc.test.cjs @@ -173,14 +173,21 @@ describe('perf #316: acquireStateLock hoists sleep buffer — exactly one SAB pe test( 'sabCount === 1 after a call that undergoes >= 1 retry (post-fix assertion)', - { timeout: 8000 }, + { timeout: 15000 }, async () => { - // ── Worker A: hold the lock for 1000ms ───────────────────────────────── + // ── Worker A: hold the lock until the writer signals contention ───────── // The holder releases the lock only when the writer signals its first // failed lock attempt (see WRITER_WORKER_CODE), so the writer is guaranteed - // to contend at least once regardless of worker-spawn latency. holdMs is now - // only a safety cap in case that signal never arrives. - const holdMs = 1000; + // to contend at least once. holdMs is ONLY a safety cap for a dead/hung + // writer — it MUST be large enough that it never elapses while Worker B is + // still spawning + requiring state.cjs + installing its stubs. Under load + // (full suite in a container) that spawn+init can exceed 1s; a 1s cap let A + // time out and remove the lock before B contended, so B's first openSync + // succeeded (lockAttempts:1) and the retry-path witness failed. 30s is + // comfortably beyond B's worst-case init yet still bounded; the test's own + // timeout caps wall-clock, and afterEach terminate()s A the moment B posts + // its result, so the normal path adds no delay. + const holdMs = 30000; const releaseSab = new SharedArrayBuffer(4); let resolveLockWritten; const lockWritten = new Promise((resolve) => { resolveLockWritten = resolve; });