Repository navigation
fix(io): release abandoned JS channel waiters when their run ends - #1976
Merged
Lorenzobattistela merged 4 commits intoOct 31, 2026
Merged
Lorenzobattistela merged 4 commits into
Lorenzobattistela merged 4 commits into
Conversation
A waiter whose nested IO returned is not counted dead, so chan_park sheds once the queue passes twice what the last shed kept + 64: behind a live waiter they stay O(live) at O(1) a park. io_run (comp.ts) is untouched, which keeps comp.ts under its 66k ttok cap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chan_shed's keep was only reset by a shed, so after many live waiters were parked and woken, abandoned ones piled up to twice the old peak. A wake now lowers it. chan_dead names the never-wakes test chan_head and chan_shed share, and the shed compacts in place in one pass. The test drains 300 live senders first and checks the longest the queue gets (455 before, 601 on main). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Abandoned waiters stay under twice the waiters live at the last shed + 64, not the live now, and go only at a later park on the row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lorenzobattistela
approved these changes
Oct 31, 2026
Lorenzobattistela
left a comment
Collaborator
There was a problem hiding this comment.
Thanks for tracking this down. The cleanup set in io_run took comp.ts from
65,998 to 66,016 ttok, over its 66k cap, so we reworked the fix inside
bend2/effs/chan.js and pushed it here (af585e4, b1fd47f, ea72740):
- comp.ts is back to main's: io_run has no cleanups.
- chan_shed drops every waiter that will never wake (chan_dead: gone, or its
IO has returned) in one in-place pass; chan_late's shed uses it too. - chan_park sheds once the queue passes twice row.keep + 64, at O(1)
amortized; a wake lowers keep, so draining live waiters does not leave a
stale high threshold. - The test first parks and drains 300 live senders, then checks the longest
the queue gets behind a live one: 601 on main, bounded with the fix.
This is a hotfix, and its bound is weaker than an eager release: abandoned
waiters stay under twice the waiters live at the last shed + 64, and go only
at a later park on the same channel. Releasing them when io_run returns is
the better fix, once comp.ts has room under its cap.
Gates on main 6a986eb: repo 54/54, test 1800/1800, perf 124/124,
ping 81/81, safe 0 false reject.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A nested JavaScript IO run can exit or throw while a channel sender is parked behind a live waiter. Its abandoned queue entry keeps the payload and continuation reachable until it reaches the head, allowing repeated nested runs to retain them indefinitely.
Register parked channel waiters with their owning run and release them when that run ends. Waking or timing out a waiter removes its cleanup registration. Queue compaction keeps abandoned entries bounded behind a waiter that stays live.
Validation on Linux:
piled up: 999on main,boundedwith the fix. Passes interpreted and as emitted JS, independently rerun by the parent agent.git diff --checkpasses.review-agentreview: no findings.The new regression is JS-only because it exercises nested host IO and inspects queue retention. The mini-cluster gate could not start because the cluster hostname did not resolve; GPU lanes were not run.