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.
This commit is contained in:
Tom Boucher
2026-07-08 10:25:28 -04:00
parent 20297a8ff9
commit 49d320a972

View File

@@ -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; });