Repository navigation
fix(Deferred): clear resumes before resuming waiters on completion - #7364
Merged
Merged
Conversation
🦋 Changeset detectedLatest commit: 27daa66 The changes in this PR will be included in the next version bump. This PR includes changesets to release 30 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Contributor
Bundle Size AnalysisGenerated from PR build output; treat the content below as untrusted.
|
This branch had an error being deployed
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.
Fixes #7365
Summary
Completing a
Deferredwhose completion effect carries an interrupt cause can permanently starve later waiters.doneUnsafeiteratesself.resumeslive:When the completion effect is e.g.
Exit.failCause(Cause.interrupt()), resuming a suspended waiter kills that waiter fiber synchronously insideresume(effect). The dying waiter'sDeferred.awaitcleanup then runs whileself.resumesis still defined and splices its own entry out of the array mid-iteration. The next waiter shifts down into the just-visited index, the loop index moves past it, and that waiter is never resumed — it hangs forever.Concretely: with two suspended waiters, waiter 0 resumes (and dies of interruption), its cleanup splices
resumes[0], waiter 1 becomesresumes[0], the loop advances toi = 1againstlength = 1, and waiter 1 is skipped. Observed empirically in[email protected].This is the starvation counterpart to #7194: that fix guarded the
_awaitcleanup against running after completion (resumes === undefined), but the cleanup still mutates the array when it fires during the completion loop.Fix
Snapshot and clear
resumesbefore resuming, matching the swap-before-resume patternLatchalready uses for exactly this reentrancy hazard:Clearing first also makes a dying waiter's cleanup a no-op during completion (the
resumes === undefinedguard from #7193 short-circuits), so the loop iterates a stable snapshot.All completion paths (
done,complete,completeWith,fail,die,interrupt,into, and externaldoneUnsafecallers inPubSub,ScopedCache,FiberMap,FiberHandle,Socket) funnel throughdoneUnsafe, so this single change covers them.Queueresumes takers viaSetiteration andLatchalready snapshots, so neither shares the bug.Reproducer
The added test forks two waiters, lets both suspend, then completes the
DeferredwithExit.failCause(Cause.interrupt()). On unpatchedmainthe second waiter never settles and the test's timeout fires; with the fix both waiters settle with the interrupt cause.Testing
pnpm vitest run --project effect test/Deferred.test.ts— new test fails before the fix, all 16 pass afterpnpm vitest run --project effect test/Latch.test.ts test/PubSub.test.ts test/Queue.test.ts test/ScopedCache.test.ts test/FiberMap.test.ts test/FiberHandle.test.ts— 204 passedpnpm checkinpackages/effect, repo lint on touched files