-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Add Task.WhenAny overloads with CancellationToken #37505
Comments
Tagging subscribers to this area: @tarekgh |
Dup of #27722? |
Not entirely, it's a separate proposal specifically for |
Came here to report this. My use case: I am repeatedly subscribing to a bunch of operations that represent events. Most of these subscriptions are unsubscribed fairly quickly. If the |
Updated proposal with some minor fixes and switched to null Task result instead of canceled Tasks (moved to alternatives section). |
Null task results could lead people to await directly without checking for null in codebases that aren't on C# 8 with NRTs enabled. Given the signature, I'd have expected a canceled outer task rather than a succeeded task with a result of null. |
Background and Motivation
It's a relatively common pattern to await on tasks which don't support cancellation (yet). One workaround is to pass
new Task(() => {}, cancellationToken)
as one of the tasks toTask.WhenAny
, but I've also seen a variant where the token registration sets a TCS result and the TCS.Task is passed to WhenAny and even more complicated variants, but all of them are pretty inefficient and make the code unwieldy.There are somewhat similar
WaitAny
overloads for sync-over-async code already.Proposed API
Usage Examples
Alternative Designs
Instead of setting a null result when canceled, could instead mark the
WhenAny
task as canceled, but this is inefficient since it throws cancellation exceptions in the common case whereWhenAny
is directly awaited. Also this has no benefits when Unwrap is used as it behaves almost the same for null task results and cancelled tasks (the only difference is cancellationToken value in the task state).Alternative #27722:
The same effect could be achieved with a general purpose WithCancellation API on tasks, which would support the previous example like this:
return Task.WhenAny(task1.WithCancellation(ct), task2.WithCancellation(ct)).Unwrap();
This could be more efficient actually, especially when the token isn't cancellable or the result is synchronously available already.
Risks
Aside from the API count increase, I see the main risk that if new CancellationTokenSource instances are created and used with these APIs, it would be quite easy to end up not disposing them properly (or disposing them too early). To avoid such pitfalls for the most common cases, a general purpose
WithTimeout(int/TimeSpan)
API on Task would be very useful, but it would cover only timeouts instead of all cancellations...The text was updated successfully, but these errors were encountered: