Skip to content

fix: handle read index overshoot - #246

Merged
mcollina merged 2 commits into
mainfrom
fix/ready-index-overshoot
Sep 2, 2026
Merged

mcollina merged 2 commits into
mainfrom
fix/ready-index-overshoot

Conversation

@mcollina

@mcollina mcollina commented Sep 1, 2026 •

Copy link
Copy Markdown
Member

Restore wait()'s not-equal result when a notified value skips the expected snapshot, allowing waitForRead() to resample instead of waiting forever. This also covers values that cycle before notification and error sentinels.

The regression was introduced by #178, whose Atomics.waitAsync refactor removed the historical not-equal result. #198 later moved the affected logic into waitForRead() without correcting it.

Fixes #245

@mcollina
mcollina requested a review from jsumners September 1, 2026 10:32

@kibertoad kibertoad left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The core fix looks correct. Atomics.waitAsync registers atomically, so a READ_INDEX change either resolves the promise with 'ok' or makes the call return synchronously with 'not-equal'; routing both back to the caller lets waitForRead re-sample WRITE_INDEX and closes the overshoot loop from #245. Full suite passes on the branch (the test/error-flush.test.js flakiness I saw also reproduces on main, so it is not from this PR).

Three comments inline, one of which I think leaves a live variant of the same bug.

Comment thread lib/wait.js Outdated
Comment thread lib/wait.js Outdated
Comment thread test/wait.test.js
@mcollina

mcollina commented Sep 2, 2026

Copy link
Copy Markdown
Member Author

Addressed all review threads in a011ff3 and added coverage for fallback-timeout changes, synchronous Atomics.waitAsync results, and end-to-end READY re-sampling. The local full suite passes (63 passed, 3 skipped), and the issue reproduction exited cleanly 20/20 times.

I also retried the failed CI jobs three times. The remaining failures are pre-existing Node 26 flakes outside this change: Windows repeatedly times out in test/end.test.js (synchronous _final support), while duplicate push runs intermittently time out in existing flush tests. The new wait and READY regression tests pass in those jobs. The latest main CI run also fails in the Node 26 Windows matrix, and the reviewer independently reproduced the existing error-flush flake on main.

@mcollina
mcollina merged commit 63ed7da into main Sep 2, 2026
81 of 98 checks passed
kolacheee added a commit to Pasta-Devs/Marinara-Engine that referenced this pull request Sep 23, 2026
Outside production the server logs through pino-pretty in a thread-stream
worker, which stays referenced until its READY handshake sees the read
index reach a write index it snapshotted earlier. thread-stream 4.2.0
compares with ===, so when logging continues during startup the read index
can jump past the snapshot, or be reset under it, and the handshake never
completes. The worker then holds the event loop open forever.

That is the image-dimension regression's hang (#6529): its retry warnings
land while the worker boots, it prints its success line, and then sits
until the runner kills it at 30 seconds. It was never a keep-alive socket;
the only live handle at hang time is the worker's MessagePort, with the
stream stuck at ready=false and read == write.

Apply upstream's fix (pinojs/thread-stream#246 and #251) as a pnpm patch
until a release after 4.2.0 ships it. Measured on this machine, the spec
alone: 11/30 runs hung unpatched, 0/40 patched.

The regression runner also names every file that did not pass, so a
timeout no longer shows up only as "1 failed".
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ThreadStream can lose 'ready' permanently when READ_INDEX overshoots the snapshot taken at READY — process never exits

3 participants