You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix: release pending stream ownership after local receive-buffer exhaustion - #842
Fix locally exhausted stream receive buffers leaving live pending-call ownership after the consumer exits. This targets dev only; no merge, main cherry-pick, or 2.0.4 publication is part of this PR.
Root cause
In PooledAsyncStreamDispatcher<T>.DispatchAcquiredAsync, exceeding the 4096-element receive buffer used Complete(ResourceExhausted). That selected the same consumer terminal state as a genuine peer StreamComplete. DisposeAsync therefore skipped ConsumerAbandonedCallback. Closing a stream dispatch state only stops future dispatch; it does not independently remove the pending call and StreamManager route. Standalone RPC cancellation, deadlines, and connection-close cleanup retain their existing independent paths.
Fix
Distinguish local receive-buffer failure from genuine peer completion with an explicit terminal value, without changing the 4096-item cap or SharpLinkErrorCode.ResourceExhausted.
After local receive failure, consumer disposal invokes the existing abandonment callback once, following dispatch drain and credit return; ClientConnection then removes the pending slot, unregisters the inbound route, and emits ConsumerAbandoned Cancel for a published request. True peer completions still skip this callback.
Keep PooledAsyncStreamDispatcher.cs within the repository's existing source LOC baseline by extracting terminal-publication code into PooledAsyncStreamDispatcher.Terminal.cs (partial class), rather than raising thresholds.
Deterministic regression evidence (test-first)
RED: test-only commit 59ab11fd4cce53e8b8cf2e7d77bdebf7f3063d69 against dev base f0fee51e77a5a736469bffa837ceb8f31b1f6877: PR Fast run 38157457266 compiled, then failed exactly 3 new regression tests (2060 passed, 3 failed of 2063): actual pending/route leak, missing local-failure callback after ResourceExhausted, and local-completion/Dispose publication race.
Candidate:493b9db62db7352ee35b379fde9f56fd2abb6687 (see current PR checks). Four focused cases cover early exit, actual pending/route cleanup and published-request Cancel, full credit return and ResourceExhausted, concurrent/duplicate Dispose with async callback, and real peer completion as a negative control. The published-frame case asserts credit-before-Cancel, without incorrectly imposing Request-before-WindowUpdate ordering: the send pump may prioritize WindowUpdate before Request, provided both precede Cancel.
Broader validation: PR Fast runs formatting, maintainability, Release build, all unit/generator/load tests, and allocation gate; PR Extended includes full integration (including generated ABI dynamic-module stream lifecycle exit modes), AOT/package/load and codec compatibility. Treat the linked checks as authoritative. A multi-hour generated-ABI soak is separate from these PR gates and is not claimed as performed here.
Review / sequencing
Review and validate this PR against dev. Do not merge automatically.
SunSi12138
changed the title
fix: clean pending stream ownership after local receive buffer failure
fix: release pending stream ownership after local receive-buffer exhaustion
Oct 11, 2026
Verification result — head eb9f20cc18c8dec88b8d0e9d5f1a74af5bef7657
Current dev: the fix branch is synchronized with devaaedfc3f80e8fc00c504a2018577218f224ec3c3 through a two-parent branch-only merge; the PR diff still contains exactly the three fix/test files. All checks below were rerun on that synchronized head and passed. The PR is open, not merged, with base dev.
Test-first RED:PR Fast 38157457266 on test-only 59ab11fd4cce53e8b8cf2e7d77bdebf7f3063d69, before the implementation fix: 2060/2063 unit tests passed, and exactly three added bug-focused tests failed (pending/route ownership, local ResourceExhausted abandonment, and local completion versus Dispose).
Fixed GREEN:
PR Fast 38159338206: 2063/2063 unit, 271/271 generator, 77/77 load-test cases passed; Release build, formatting, maintainability, reference-boundary and allocation checks passed.
PR Extended 38159360891: 454/454 integration cases passed (the full integration suite includes the generated API4 collectible dynamic-module streaming exit lifecycle cases for normal completion, cancellation before first item, cancellation mid-stream, consumer break, and service exception); Debug admission race, Release build, AOT transport/topology smoke, NuGet packaging and smoke, one-way demo, load smoke, and cross-platform codec compatibility all passed.
The published-request regression confirmed the outbound pump may prioritize WindowUpdate ahead of Request; the required invariant tested here is that both precede the single ConsumerAbandoned Cancel. No thresholds, deadlines, buffer caps or assertions were relaxed to suppress the original bug.
Scope: no main patch and no merge. A separate multi-hour generated-ABI soak was not executed on this PR; the generated-ABI dynamic-module lifecycle integration tests above were executed and passed. This PR is ready for human review.
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
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
Fix locally exhausted stream receive buffers leaving live pending-call ownership after the consumer exits. This targets
devonly; no merge,maincherry-pick, or 2.0.4 publication is part of this PR.Root cause
In
PooledAsyncStreamDispatcher<T>.DispatchAcquiredAsync, exceeding the 4096-element receive buffer usedComplete(ResourceExhausted). That selected the same consumer terminal state as a genuine peerStreamComplete.DisposeAsynctherefore skippedConsumerAbandonedCallback. Closing a stream dispatch state only stops future dispatch; it does not independently remove the pending call andStreamManagerroute. Standalone RPC cancellation, deadlines, and connection-close cleanup retain their existing independent paths.Fix
SharpLinkErrorCode.ResourceExhausted.ClientConnectionthen removes the pending slot, unregisters the inbound route, and emitsConsumerAbandoned Cancelfor a published request. True peer completions still skip this callback.PooledAsyncStreamDispatcher.cswithin the repository's existing source LOC baseline by extracting terminal-publication code intoPooledAsyncStreamDispatcher.Terminal.cs(partial class), rather than raising thresholds.Deterministic regression evidence (test-first)
59ab11fd4cce53e8b8cf2e7d77bdebf7f3063d69againstdevbasef0fee51e77a5a736469bffa837ceb8f31b1f6877: PR Fast run 38157457266 compiled, then failed exactly 3 new regression tests (2060 passed, 3 failed of 2063): actual pending/route leak, missing local-failure callback afterResourceExhausted, and local-completion/Dispose publication race.493b9db62db7352ee35b379fde9f56fd2abb6687(see current PR checks). Four focused cases cover early exit, actual pending/route cleanup and published-request Cancel, full credit return andResourceExhausted, concurrent/duplicate Dispose with async callback, and real peer completion as a negative control. The published-frame case asserts credit-before-Cancel, without incorrectly imposing Request-before-WindowUpdate ordering: the send pump may prioritize WindowUpdate before Request, provided both precede Cancel.Review / sequencing
dev. Do not merge automatically.mainpatch PR for the not-yet-published 2.0.4 release. PR chore(release): promote reviewed dev fixes for 2.0.4 #804 is already merged and is not this fix's feature branch.