Summary
for await (const chunk of process.stdin) on a pipe intermittently stops delivering chunks and never resumes. The process sits idle (0% CPU) with input still unread, the writer blocks, and nothing ever completes. It is a race: 1 MiB stalls about half the time, 4 MiB and up essentially always. 64 KiB is fine.
Repro
// rd.ts
import * as process from "node:process";
async function main(): Promise<void> {
let total = 0, chunks = 0;
for await (const data of process.stdin) { total += data.length; chunks++; }
console.log("total", total, "chunks", chunks);
}
main();
# drive.py — writes N bytes to stdin, closes it, waits 12 s
import subprocess
def run(cmd, size):
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
try: out, err = p.communicate(bytes([1]) * size, timeout=12); st = 'done'
except subprocess.TimeoutExpired: p.kill(); out, err = p.communicate(); st = 'HANG'
return f'{st} {out.decode().strip()}'
for size in (65536, 1 << 20, 3 << 19, 2 << 20, 4 << 20):
print(size, run(['./rd'], size), '|', run(['./rd'], size))
perry 0.5.1618 (b9ba951ff8, macOS arm64, 16 KiB pipe chunks), two runs per size:
| bytes |
run 1 |
run 2 |
| 65,536 |
done, 7 chunks |
done, 7 chunks |
| 1,048,576 |
HANG |
done, 170 chunks |
| 1,572,864 |
done, 239 chunks |
HANG |
| 2,097,152 |
done, 323 chunks |
HANG |
| 4,194,304 |
HANG |
HANG |
| 8,388,608 |
HANG ×3 |
|
node 24 on the same driver: done total 8388608 chunks 8261.
When stalled, a sample shows the main thread in pthread_cond_wait and the stdin reader thread in read(). Logging per chunk shows delivery simply stops part-way (e.g. at 890,364 of 2,097,151 bytes), so it looks like a lost wakeup / pause-resume hand-off between the reader thread and the loop rather than EOF handling. Linux not tested yet.
Also seen downstream, not yet isolated (may be separate bugs)
Same stdin path inside the Native Messaging host from https://github.com/guest271314/NativeMessagingHosts (each chunk copied into one large Uint8Array with .set, then echoed with stdout.write):
- 2 MiB message: one run hung, one run finished but emitted a frame whose 4-byte length header was garbage (
4022190063) — i.e. corrupted output, not just a stall.
- 64 MiB message:
SIGSEGV (rc −11) after ~75 ms on one run, hang on another.
Those need their own reduction once the stall is out of the way; noting them here so they are not lost.
Impact
Any CLI / filter / Native Messaging host that reads more than about a megabyte from a pipe. Browser→host Native Messaging messages go up to 64 MiB.
Related: #10873 and the Uint8Array<ArrayBuffer> indexOf issue (same program). Context: jlucaso1/js-compiled#2
Summary
for await (const chunk of process.stdin)on a pipe intermittently stops delivering chunks and never resumes. The process sits idle (0% CPU) with input still unread, the writer blocks, and nothing ever completes. It is a race: 1 MiB stalls about half the time, 4 MiB and up essentially always. 64 KiB is fine.Repro
perry 0.5.1618 (
b9ba951ff8, macOS arm64, 16 KiB pipe chunks), two runs per size:node 24 on the same driver:
done total 8388608 chunks 8261.When stalled, a
sampleshows the main thread inpthread_cond_waitand the stdin reader thread inread(). Logging per chunk shows delivery simply stops part-way (e.g. at 890,364 of 2,097,151 bytes), so it looks like a lost wakeup / pause-resume hand-off between the reader thread and the loop rather than EOF handling. Linux not tested yet.Also seen downstream, not yet isolated (may be separate bugs)
Same stdin path inside the Native Messaging host from https://github.com/guest271314/NativeMessagingHosts (each chunk copied into one large
Uint8Arraywith.set, then echoed withstdout.write):4022190063) — i.e. corrupted output, not just a stall.SIGSEGV(rc −11) after ~75 ms on one run, hang on another.Those need their own reduction once the stall is out of the way; noting them here so they are not lost.
Impact
Any CLI / filter / Native Messaging host that reads more than about a megabyte from a pipe. Browser→host Native Messaging messages go up to 64 MiB.
Related: #10873 and the
Uint8Array<ArrayBuffer>indexOfissue (same program). Context: jlucaso1/js-compiled#2