Repository navigation
Conversation
|
Updated 6:45 PM PT - Sep 23rd, 2026
✅ @robobun, your commit 059ad0c8dcd6cb7a65e3cb0dfca7e382e3e91cf2 passed in 🧪 To try this PR locally: bunx bun-pr 43862That installs a local version of the PR into your bun-43862 --bun |
…n pipe "a body over a FIFO opened by path is still teed" fails in two ways in the parallel batch. It times out at the bun:test deadline with "killed 1 dangling process", mostly on alpine aarch64. Its first statement was Bun.spawnSync(["mkfifo", fifo]). That call spins when a GC finalizer has left the poll count of spawnSync's isolated loop off (#40078). The test now creates the FIFO with the mkfifo test helper, like fetch.test.ts and streams.test.js, and no longer calls spawnSync. It fails with "EPIPE: broken pipe, write" when the test process stalls after the spawn. cloneInChild() wrote to the stdin of every child, but the FIFO child never reads stdin and can exit first. The helper now pipes stdin only to a child that reads it.
f78f221 to
acc16d6
Compare
|
Warning Review limit reached
This review includes 1 billable file and costs up to $0.25.
Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing. Or wait 3 minutes for your next included review. View limit detailsLimit details: You’ve used all 10 included reviews currently available. Review configuration: ⚙️ Run configurationConfiguration used: Repository: oven-sh/bun/.coderabbit.yaml Review profile: ASSERTIVE Plan: Essentials Run ID: 📒 Files selected for processing (1)
Comment |
|
Status Both failures of the FIFO test reproduce on linux x64 (release canaries 6808912 musl and 367d939 glibc, and a debug build):
With this PR the FIFO test passes in both setups. The spawnSync spin itself is the subject of #40078, not of this PR. PR: #43862 |
There was a problem hiding this comment.
Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.
LGTM — small, test-only change that swaps a spawned mkfifo for the in-repo FFI helper and stops feeding stdin to a child that never reads it; the inline note is a naming nit, not a correctness problem.
Extended reasoning...
The diff touches only test/js/web/fetch/body-clone.test.ts: cloneInChild gains an options object with feedStdin, and the FIFO test creates its FIFO via the existing mkfifo helper (test/mkfifo.ts, mapped in test/tsconfig.json and already used by fetch.test.ts and others). All three call sites of cloneInChild were checked — the two stdin tests pass no second argument and keep the default pipe, and the FIFO test uses the new { args, feedStdin: false } shape, so no positional array is silently dropped. With stdin: feedStdin ? "pipe" : "ignore" the proc.stdin type resolves to FileSink | undefined via WritableToIO, so the if (stdin) guard narrows correctly and the writes are skipped only for the FIFO child. No security-sensitive surface is touched, no CODEOWNERS entry covers this path, and the file is not listed in test/expectations.txt changes; the only surviving finding is the misleading bothBodiesReadStdin name applied to the FIFO child, which is cosmetic.
Additional findings (outside the current diff — GitHub can't attach inline comments there):
-
🟡
test/js/web/fetch/body-clone.test.ts— A reader of the FIFO test is told its child read stdin when it has no stdin at all, which misleads the next person debugging this file. The expected object at test/js/web/fetch/body-clone.test.ts:1170 is namedbothBodiesReadStdin, and :1189 asserts the FIFO child (spawned withstdin: "ignore"at :1156) against it. On base the FIFO child at least had a stdin pipe; this diff makes the name contradict the code. Fix: rename to something store-neutral such asbothBodiesSeeWholeInput(and use it at :1173, :1177, :1189), or give the FIFO test its own expected object.Why this was flagged
test/js/web/fetch/body-clone.test.ts:1170 defines
bothBodiesReadStdin. This diff changes the FIFO child tostdin: "ignore"(:1156 withfeedStdin: falseat :1189) so that child cannot read stdin, yet :1189 still assertstoEqual(bothBodiesReadStdin). The repo's landing rules (as summarized to this review) say a name or comment that contradicts code is a correctness bug for readers, and the PR description's own sweep for stale state missed it. Population: every maintainer reading this describe block; rate: each time the test is read or extended. The dismissal said base already reused the name, but base's FIFO child did receive a stdin pipe with 'hello world' written to it, so the name was only misleading there and is outright false now. Remedy: rename the constant to describe the invariant (both bodies see the whole input) rather than the input source.Verification: nit — triggered whenever someone reads the FIFO test; nothing executes wrongly. Verified in /home/claude/bun/test/js/web/fetch/body-clone.test.ts: line 1170
const bothBodiesReadStdin = { stdout: \["hello world","hello world"]`, stderr: "", exitCode: 0 };; line 1189-1191 asserts the FIFO child's resulttoEqual(bothBodiesReadStdin)while passingfeedStdin: false`, which line 1156…
The FIFO child has no stdin, so bothBodiesReadStdin no longer described it. Both bodies see the whole input, whatever the store is.
|
Renamed the constant to |
There was a problem hiding this comment.
LGTM — small, test-only change that swaps a spawned mkfifo for the in-process FFI helper and stops feeding stdin to the FIFO-reading child.
What was reviewed:
cloneInChildin test/js/web/fetch/body-clone.test.ts: withfeedStdin: falsethe child getsstdin: "ignore"andproc.stdinis undefined, so theif (stdin)guard skips the write/flush/end; the two stdin tests keep the default and are unchanged in behavior.- The
mkfifoimport resolves through test/tsconfig.json to test/mkfifo.ts, the same helper fetch.test.ts already uses; it throws on failure rather than returning silently, so the removedexitCodeassertion is not a lost check. - FIFO ordering: the
sh -c printfwriter is spawned before the reader child, both ends open before either proceeds, andwriter.exitedis still asserted; the test stays gated bytest.skipIf(isWindows).
Extended reasoning...
The diff touches one test file, test/js/web/fetch/body-clone.test.ts, across two commits: it replaces Bun.spawnSync(["mkfifo"]) with the in-process mkfifo helper, adds a feedStdin option to cloneInChild so the FIFO child is spawned with stdin "ignore" and receives no pipe writes, and renames the shared expected-result constant. No runtime source is changed and no security-sensitive surface is involved. The change follows an existing pattern in fetch.test.ts, the file is not covered by CODEOWNERS, the bug hunt ran to a dry streak with no findings, and the only commit after the earlier automated comment is the constant rename, so an approval is a reasonable decision here.
Problem
test/js/web/fetch/body-clone.test.ts, test "a body over a FIFO opened by path is still teed", times out at the 90000 ms deadline withkilled 1 dangling processin 35 of 85 recent builds (119960 to 120061), 33 on alpine 3.23 aarch64. Its first statement,Bun.spawnSync({ cmd: ["mkfifo", fifo] }), spins (the bug of spawnSync: release polls and keep-alives on the loop they counted on #40078). The dangling process is themkfifozombie.EPIPE: broken pipe, writeon debian lanes (build 119808).cloneInChild()writes to the stdin of every child, but the FIFO child never reads stdin. If the test process stalls after the spawn, the child exits first andflush()rejects.Fix
mkfifohelper (test/mkfifo.ts), asfetch.test.tsdoes. The file no longer callsspawnSync.cloneInChild()takesfeedStdin. The FIFO child getsstdin: "ignore"and no writes.bun bd test test/js/web/fetch/body-clone.test.ts(85 pass). The Notes reproduce each failure with the old test.Background
Bun.spawnSyncwaits on a private event loop. A loop whose poll count is 0 returns from its tick withoutepoll_wait. spawnSync: release polls and keep-alives on the loop they counted on #40078 shows how a GC finalizer that runs during the wait subtracts a main-loop poll from the private loop. A laterspawnSyncthen never sees its child exit.Response.clone()over a FIFO, not about how the FIFO is made.Downsides
Notes
Timeout, reproduced with the unmodified test. Put the fixture of #40078 (three
Bun.stderr.writer()sinks that the GC finalizes inside a warmed-upspawnSync) as a test in front of the FIFO test. Run with stderr on a pipe:This is the CI output (build 120061, alpine 3.23 aarch64, shard 5:
killed 1 dangling process,[90001.50ms],this test timed out after 90000ms). Release canary 6808912 (linux x64 musl), 367d939 (linux x64 glibc) and a debug build all give it. With the test of this PR behind the same fixture, the FIFO test passes (37 ms on the release build, 1.6 s on the debug build).Why the CI history looks the way it does. The spin needs a GC sweep inside an earlier
spawnSyncof the same worker process, so it follows allocation history. Workers take the sorted file list in contiguous ranges.body-clone.test.tsis atest/js/third_party/jsonwebtoken/*.test.jsfile. In the two buckets from the quiet window (builds 119100, 119300) it isgrpc-js/test-deadline.test.ts.bun test --parallel=3(musl canary, busyboxsh), 0 of 1800 runs of the three-process scenario under load.EPIPE. A busy wait of 30 ms between
Bun.spawn()and the firstproc.stdin.write()in the old helper givesEPIPEatflush()in 12 of 12 runs (musl release build). Without the wait it shows up in 48 of 1500 runs under load, atend()or atflush(). The two stdin tests keep the pipe: their child reads stdin to EOF, so it cannot exit beforeend().no test proof · iteration 0 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/js/web/fetch/body-clone.test.ts