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

This patch fixes an issue where the `CancellationToken::run_until_cancelled` never cancels the `Future` that returns `Ready` at the first `poll`.

---------

Co-authored-by: Luca BRUNO <[email protected]>
This commit is contained in:
Stepan Tubanov
2025-07-20 10:03:29 +08:00
committed by GitHub
co-authored by Luca BRUNO
parent 0a3fe46086
commit 6d868d96ce
2 changed files with 35 additions and 4 deletions
+20 -4
View File
@@ -268,6 +268,12 @@ impl CancellationToken {
/// unless the [`CancellationToken`] is cancelled. In that case the function returns
/// `None` and the future gets dropped.
///
/// # Fairness
///
/// Calling this on an already-cancelled token directly returns `None`.
/// For all subsequent polls, in case of concurrent completion and
/// cancellation, this is biased towards the future completion.
///
/// # Cancellation safety
///
/// This method is only cancel safe if `fut` is cancel safe.
@@ -303,11 +309,15 @@ impl CancellationToken {
}
}
RunUntilCancelledFuture {
cancellation: self.cancelled(),
future: fut,
if self.is_cancelled() {
None
} else {
RunUntilCancelledFuture {
cancellation: self.cancelled(),
future: fut,
}
.await
}
.await
}
/// Runs a future to completion and returns its result wrapped inside of an `Option`
@@ -316,6 +326,12 @@ impl CancellationToken {
///
/// The function takes self by value and returns a future that owns the token.
///
/// # Fairness
///
/// Calling this on an already-cancelled token directly returns `None`.
/// For all subsequent polls, in case of concurrent completion and
/// cancellation, this is biased towards the future completion.
///
/// # Cancellation safety
///
/// This method is only cancel safe if `fut` is cancel safe.
@@ -492,6 +492,21 @@ fn run_until_cancelled_test() {
fut.as_mut().poll(&mut Context::from_waker(&waker))
);
}
// Do not poll the future when token is already cancelled.
{
let token = CancellationToken::new();
let fut = token.run_until_cancelled(async { panic!("fut polled after cancellation") });
pin!(fut);
token.cancel();
assert_eq!(
Poll::Ready(None),
fut.as_mut().poll(&mut Context::from_waker(&waker))
);
}
}
#[test]