Resolve #2733: Remove abort listener after synchronous callback throw#2759
Merged
Conversation
raceWithAbort(fn, signal) registered the abort listener before invoking fn() and relied on Promise.resolve(fn()).finally(...) to remove it. When fn() threw synchronously, the throw escaped before Promise.resolve could wrap it, so the finally cleanup never ran and the listener leaked. Convert the fn() invocation into a settled promise via try/catch so a synchronous throw flows through the same finally cleanup that removes the abort listener. Add regression tests covering sync-throw listener removal and post-throw abort behavior, and document the contract in the runtime README (EN/KO).
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.
Summary
Linked context: Closes #2733
raceWithAbort(fn, signal)registered its abort listener before invokingfn()and relied onPromise.resolve(fn()).finally(...)to remove it. Whenfn()threw synchronously, the throw escaped beforePromise.resolvecould wrap it, so thefinallycleanup never ran and the abort listener leaked on the signal. Repeated failed operations could retain listeners and leaked references.Changes
packages/runtime/src/abort.ts: Convert thefn()invocation into a settled promise viatry/catchso a synchronous throw flows through the samefinallycleanup that removes the abort listener.Promise.resolve()only wraps an already-produced value; it does not catch a throw emitted whilefn()is invoked, so the explicittry/catchis required.packages/runtime/src/abort.test.ts: New regression test suite coveringraceWithAbortandcreateAbortError, including sync-throw listener removal, post-throw abort behavior, and normal resolve/abort paths.packages/runtime/README.md/README.ko.md: Document the behavioral contract that the abort listener is always removed oncefnsettles, including synchronous throws..changeset/race-with-abort-remove-listener-sync-throw.md: Patch changeset for@fluojs/runtimebecause abort helpers are public runtime integration behavior.Testing
pnpm vitest run packages/runtime/src/abort.test.ts— 9 tests passed.pnpm --filter @fluojs/runtime test— 283 tests passed (24 files).pnpm --filter @fluojs/runtime typecheck— passed.pnpm exec biome lint packages/runtime/src/abort.ts packages/runtime/src/abort.test.ts— passed.pnpm verify:public-export-tsdoc— passed (changed mode).pnpm verify:platform-consistency-governance— passed.Release impact
Patch changeset for
@fluojs/runtime: abort helpers are public runtime integration behavior and the listener-leak fix changes cleanup semantics for synchronous-throw callers.Public export documentation
@param/@returnstags where applicable.@exampleblocks and README scenario examples still play complementary roles.The
raceWithAbortTSDoc was extended to document the synchronous-throw cleanup guarantee and the re-throw behavior.Behavioral contract
New behavioral contract:
raceWithAbort(fn, signal)always removes its abort listener oncefnsettles, including whenfnthrows synchronously before returning a promise. Documented inpackages/runtime/README.mdandREADME.ko.md, covered bypackages/runtime/src/abort.test.ts.Platform consistency governance (SSOT)
createPlatformConformanceHarness(...)for platform component contracts orcreateHttpAdapterPortabilityHarness(...)for HTTP adapter portability contracts.README EN/KO mirrors both received the new behavioral contract bullet. No platform contract docs changed. No platform harness conformance claims were added.