Skip to content

sync: fix CancellationToken failing to cancel the ready futures #7462

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

Merged
merged 3 commits into from
Jul 20, 2025

Conversation

stepantubanov
Copy link
Contributor

When token is already cancelled, provided future should not be polled.

Motivation

Example: We have a loop where we get something from a stream but want to exit in case of shutdown:

loop {
  let Some(item) = shutdown.run_until_cancelled(stream.next()).await else {
     info!("Exit due to shutdown");
     break;
  };
  ...
}

With current logic, as long as the stream is yielding new items, the loop is not terminating even after the token is cancelled, which seems like a bug to me.

Solution

I've changed the order of if branches to make it check the token before polling the future.

@ADD-SP ADD-SP added C-bug Category: This is a bug. A-tokio-util Area: The tokio-util crate M-sync Module: tokio/sync labels Jul 17, 2025
Copy link
Member

@ADD-SP ADD-SP left a comment

Choose a reason for hiding this comment

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

Overall looks good to me.

When token is already cancelled, provided future should not be polled.
@stepantubanov stepantubanov force-pushed the fix-cancellation-token-poll branch from 12e1569 to 0f71693 Compare July 17, 2025 08:19
ADD-SP
ADD-SP previously approved these changes Jul 17, 2025
Copy link
Member

@ADD-SP ADD-SP left a comment

Choose a reason for hiding this comment

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

Thanks!

@lucab
Copy link
Contributor

lucab commented Jul 17, 2025

Casual observer here, but I noticed that here is an explicit comment here about the bias of this polling-order:

pub async fn run_until_cancelled<F>(&self, fut: F) -> Option<F::Output>
where
F: Future,
{
pin_project! {
/// A Future that is resolved once the corresponding [`CancellationToken`]
/// is cancelled or a given Future gets resolved. It is biased towards the
/// Future completion.
#[must_use = "futures do nothing unless polled"]
struct RunUntilCancelledFuture<'a, F: Future> {

And I think this bias was explicitly requested here: #6618 (comment)

As an additional random thought, if the problem is related to calling run_until_cancelled() in a loop on a token that is already cancelled, does it maybe make sense to keep the current order bias but check for is_cancelled() upfront before (conditionally) constructing the RunUntilCancelledFuture?

@ADD-SP
Copy link
Member

ADD-SP commented Jul 17, 2025

And I think this bias was explicitly requested here: #6618 (comment)

This is a good finding, I didn't noticed this, and I think we should also change this comment because:

  1. The docs doesn't mention this biased behavior.
  2. This biased behavior is likely surprising the downstream.

I'm also open for keeping the existing biased behavior for better performance of happy path, but writing a warning about this behavior and suggesting a recommended approach to deal with this.

@lucab
Copy link
Contributor

lucab commented Jul 17, 2025

As an additional random thought, if the problem is related to calling run_until_cancelled() in a loop on a token that is already cancelled, does it maybe make sense to keep the current order bias but check for is_cancelled() upfront before (conditionally) constructing the RunUntilCancelledFuture?

I tried to implement this and check it against the newly added test. I posted my changes to https://github.com/stepantubanov/tokio/pull/1/files in case you want to go that way.

sync: fix and document cancellation bias
@stepantubanov
Copy link
Contributor Author

@lucab Thanks, seems like a good way to preserve the bias while correcting the behavior for this scenario.

@ADD-SP ADD-SP dismissed their stale review July 18, 2025 02:33

there is a brand new fix

Copy link
Member

@ADD-SP ADD-SP left a comment

Choose a reason for hiding this comment

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

@stepantubanov @lucab Thank you both for your contributions, this is a better solution that I didn't realize before.

@ADD-SP ADD-SP changed the title sync: fix CancellationToken::run_until_cancelled sync: fix CancellationToken failing to cancel the ready futures Jul 20, 2025
@ADD-SP ADD-SP merged commit 6d868d9 into tokio-rs:master Jul 20, 2025
86 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-tokio-util Area: The tokio-util crate C-bug Category: This is a bug. M-sync Module: tokio/sync
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants