Skip to content

Conversation

stephentoub
Copy link
Member

OutputAvailableAsync is not unregistering from the supplied CancellationToken. If a cancelable token is supplied and is long lived, each call with that token to OutputAvailableAsync will add another callback into that token, and that will continue to grow until either the token is dropped or has been cancellation requested. For a long-lived cancellation token, this is akin to a leak.

The implementation was trying to be too clever in avoiding an additional continuation that was previously there. However, this continuation makes it a lot easier to avoid possible deadlocks that can occur if a cancellation request comes in concurrently with a message being pushed. Instead of trying to avoid it, just use an async method, which still incurs the extra task but does so with less allocation and greatly simplifies the code while also fixing the issue, as all cleanup can now be done in the continuation as part of the async method.

Backport of #99632

Customer Impact

Unbounded memory growth if a long-lived CancellationToken is used with OutputAvailableAsync; each call would end up rooting a small object into the token.

Testing

Normal CI testing, plus local stress testing to show that the leak no longer exists.

Risk

Low

…dotnet#99632)

OutputAvailableAsync is not unregistering from the supplied CancellationToken. If a cancelable token is supplied and is long lived, each call with that token to OutputAvailableAsync will add another callback into that token, and that will continue to grow until either the token is dropped or has been cancellation requested.  For a long-lived cancellation token, this is akin to a leak.

The implementation was trying to be too clever in avoiding an additional continuation that was previously there. However, this continuation makes it a lot easier to avoid possible deadlocks that can occur if a cancellation request comes in concurrently with a message being pushed.  Instead of trying to avoid it, just use an async method, which still incurs the extra task but does so with less allocation and greatly simplifies the code while also fixing the issue, as all cleanup can now be done in the continuation as part of the async method.
@stephentoub stephentoub added the Servicing-consider Issue for next servicing release review label May 17, 2024
@stephentoub stephentoub added this to the 8.0.x milestone May 17, 2024
Copy link
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

@ericstj ericstj added Servicing-approved Approved for servicing release and removed Servicing-consider Issue for next servicing release review labels May 22, 2024
@ericstj
Copy link
Member

ericstj commented May 22, 2024

Approved in mail.

@ericstj
Copy link
Member

ericstj commented May 22, 2024

Needs package authoring.

Copy link
Member

@ericstj ericstj left a comment

Choose a reason for hiding this comment

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

LGTM

@stephentoub
Copy link
Member Author

Thanks for taking care of the package authoring, @ericstj.

@stephentoub stephentoub merged commit 48a9496 into dotnet:release/8.0-staging May 29, 2024
@stephentoub stephentoub deleted the backport99632 branch May 29, 2024 01:41
@github-actions github-actions bot locked and limited conversation to collaborators Jun 28, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Threading Servicing-approved Approved for servicing release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants