Skip to content

for await (const chunk of process.stdin) intermittently stalls forever on piped input ≥ ~1 MiB (4 MiB+ hangs ~always) #10895

Description

@proggeramlug

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions