Skip to content

Commit 6d868d9

Browse files
stepantubanovlucab
andauthored
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]>
1 parent 0a3fe46 commit 6d868d9

File tree

2 files changed

+35
-4
lines changed

2 files changed

+35
-4
lines changed

tokio-util/src/sync/cancellation_token.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,12 @@ impl CancellationToken {
268268
/// unless the [`CancellationToken`] is cancelled. In that case the function returns
269269
/// `None` and the future gets dropped.
270270
///
271+
/// # Fairness
272+
///
273+
/// Calling this on an already-cancelled token directly returns `None`.
274+
/// For all subsequent polls, in case of concurrent completion and
275+
/// cancellation, this is biased towards the future completion.
276+
///
271277
/// # Cancellation safety
272278
///
273279
/// This method is only cancel safe if `fut` is cancel safe.
@@ -303,11 +309,15 @@ impl CancellationToken {
303309
}
304310
}
305311

306-
RunUntilCancelledFuture {
307-
cancellation: self.cancelled(),
308-
future: fut,
312+
if self.is_cancelled() {
313+
None
314+
} else {
315+
RunUntilCancelledFuture {
316+
cancellation: self.cancelled(),
317+
future: fut,
318+
}
319+
.await
309320
}
310-
.await
311321
}
312322

313323
/// Runs a future to completion and returns its result wrapped inside of an `Option`
@@ -316,6 +326,12 @@ impl CancellationToken {
316326
///
317327
/// The function takes self by value and returns a future that owns the token.
318328
///
329+
/// # Fairness
330+
///
331+
/// Calling this on an already-cancelled token directly returns `None`.
332+
/// For all subsequent polls, in case of concurrent completion and
333+
/// cancellation, this is biased towards the future completion.
334+
///
319335
/// # Cancellation safety
320336
///
321337
/// This method is only cancel safe if `fut` is cancel safe.

tokio-util/tests/sync_cancellation_token.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,21 @@ fn run_until_cancelled_test() {
492492
fut.as_mut().poll(&mut Context::from_waker(&waker))
493493
);
494494
}
495+
496+
// Do not poll the future when token is already cancelled.
497+
{
498+
let token = CancellationToken::new();
499+
500+
let fut = token.run_until_cancelled(async { panic!("fut polled after cancellation") });
501+
pin!(fut);
502+
503+
token.cancel();
504+
505+
assert_eq!(
506+
Poll::Ready(None),
507+
fut.as_mut().poll(&mut Context::from_waker(&waker))
508+
);
509+
}
495510
}
496511

497512
#[test]

0 commit comments

Comments
 (0)