From e36420e6d5a436b87873f099f52cfbe3f59f50de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 14 Jul 2026 14:59:20 +0200 Subject: [PATCH] =?UTF-8?q?test(gap):=20de-flake=20test=5Fgap=5Ffs=5Ffd=5F?= =?UTF-8?q?2749=20=E2=80=94=20it=20raced=20the=20libuv=20threadpool?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The three callback-form fd mutators are dispatched to the libuv threadpool concurrently, so their callbacks fire in completion order, which is not deterministic. The gap harness compares perry against a freshly run node oracle -- and the *oracle itself* is unstable: across 40 runs of `node --experimental-strip-types` this test produced 4 distinct outputs, emitting `fchown closed cb` before `futimes closed cb` on roughly one run in six. Perry is deterministic here (always submission order), so the test red-lit at random on unrelated PRs -- it took out #6397, #6398 and #6399 today, and cost a full bisect before the oracle was suspected. The test is about the error surface (err.code / err.syscall) of each fd mutator, not about libuv's scheduling. Buffer the three callback results and flush them in a fixed order once all three have landed. Every assertion is preserved and the emitted 10 lines are byte-for-byte what they were; only the print order is pinned. After: node emits 1 distinct output across 60 runs, perry 1 across 40, and the two are byte-identical. --- test-files/test_gap_fs_fd_2749.ts | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/test-files/test_gap_fs_fd_2749.ts b/test-files/test_gap_fs_fd_2749.ts index 77f53482bf..bcfc9379f2 100644 --- a/test-files/test_gap_fs_fd_2749.ts +++ b/test-files/test_gap_fs_fd_2749.ts @@ -33,11 +33,26 @@ function syncCode(label: string, fn: () => void): void { } } +// The three callback-form calls below are dispatched to the libuv threadpool +// concurrently, so their callbacks fire in *completion* order, which is not +// deterministic — node itself emits `fchown closed cb` before `futimes closed +// cb` on roughly one run in six. Comparing raw interleaving against a freshly +// run node oracle therefore fails at random on any PR. +// +// What this test is actually about is the error surface (`err.code` / +// `err.syscall`) of each fd mutator, not libuv's scheduling. So buffer the +// three results and flush them in a fixed order once all three have landed. +// Every assertion is preserved; only the print order is pinned. +const CB_ORDER = ["ftruncate closed cb", "futimes closed cb", "fchown closed cb"]; +const cbLines = new Map(); + function cbCode(label: string, err: any): void { - if (err) { - console.log(label + ": " + err.code + " " + err.syscall); - } else { - console.log(label + ": OK"); + cbLines.set(label, err ? label + ": " + err.code + " " + err.syscall : label + ": OK"); + if (cbLines.size < CB_ORDER.length) { + return; + } + for (const key of CB_ORDER) { + console.log(cbLines.get(key)); } }