diff --git a/tokio-util/src/sync/cancellation_token.rs b/tokio-util/src/sync/cancellation_token.rs index 40a8ae867b6..d785ed424de 100644 --- a/tokio-util/src/sync/cancellation_token.rs +++ b/tokio-util/src/sync/cancellation_token.rs @@ -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. @@ -293,21 +299,25 @@ impl CancellationToken { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); - if this.cancellation.poll(cx).is_ready() { - Poll::Ready(None) - } else if let Poll::Ready(res) = this.future.poll(cx) { + if let Poll::Ready(res) = this.future.poll(cx) { Poll::Ready(Some(res)) + } else if this.cancellation.poll(cx).is_ready() { + Poll::Ready(None) } else { Poll::Pending } } } - 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.