diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:57:31 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:57:31 +0000 |
commit | dc0db358abe19481e475e10c32149b53370f1a1c (patch) | |
tree | ab8ce99c4b255ce46f99ef402c27916055b899ee /vendor/tokio/src/time/timeout.rs | |
parent | Releasing progress-linux version 1.71.1+dfsg1-2~progress7.99u1. (diff) | |
download | rustc-dc0db358abe19481e475e10c32149b53370f1a1c.tar.xz rustc-dc0db358abe19481e475e10c32149b53370f1a1c.zip |
Merging upstream version 1.72.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/tokio/src/time/timeout.rs')
-rw-r--r-- | vendor/tokio/src/time/timeout.rs | 99 |
1 files changed, 84 insertions, 15 deletions
diff --git a/vendor/tokio/src/time/timeout.rs b/vendor/tokio/src/time/timeout.rs index 61964ad24..52ab9891c 100644 --- a/vendor/tokio/src/time/timeout.rs +++ b/vendor/tokio/src/time/timeout.rs @@ -4,20 +4,39 @@ //! //! [`Timeout`]: struct@Timeout -use crate::time::{error::Elapsed, sleep_until, Duration, Instant, Sleep}; +use crate::{ + runtime::coop, + time::{error::Elapsed, sleep_until, Duration, Instant, Sleep}, + util::trace, +}; use pin_project_lite::pin_project; use std::future::Future; use std::pin::Pin; use std::task::{self, Poll}; -/// Require a `Future` to complete before the specified duration has elapsed. +/// Requires a `Future` to complete before the specified duration has elapsed. /// /// If the future completes before the duration has elapsed, then the completed /// value is returned. Otherwise, an error is returned and the future is /// canceled. /// -/// # Cancelation +/// Note that the timeout is checked before polling the future, so if the future +/// does not yield during execution then it is possible for the future to complete +/// and exceed the timeout _without_ returning an error. +/// +/// This function returns a future whose return type is [`Result`]`<T,`[`Elapsed`]`>`, where `T` is the +/// return type of the provided future. +/// +/// If the provided future completes immediately, then the future returned from +/// this function is guaranteed to complete immediately with an [`Ok`] variant +/// no matter the provided duration. +/// +/// [`Ok`]: std::result::Result::Ok +/// [`Result`]: std::result::Result +/// [`Elapsed`]: crate::time::error::Elapsed +/// +/// # Cancellation /// /// Cancelling a timeout is done by dropping the future. No additional cleanup /// or other work is required. @@ -45,24 +64,56 @@ use std::task::{self, Poll}; /// } /// # } /// ``` -pub fn timeout<T>(duration: Duration, future: T) -> Timeout<T> +/// +/// # Panics +/// +/// This function panics if there is no current timer set. +/// +/// It can be triggered when [`Builder::enable_time`] or +/// [`Builder::enable_all`] are not included in the builder. +/// +/// It can also panic whenever a timer is created outside of a +/// Tokio runtime. That is why `rt.block_on(sleep(...))` will panic, +/// since the function is executed outside of the runtime. +/// Whereas `rt.block_on(async {sleep(...).await})` doesn't panic. +/// And this is because wrapping the function on an async makes it lazy, +/// and so gets executed inside the runtime successfully without +/// panicking. +/// +/// [`Builder::enable_time`]: crate::runtime::Builder::enable_time +/// [`Builder::enable_all`]: crate::runtime::Builder::enable_all +#[track_caller] +pub fn timeout<F>(duration: Duration, future: F) -> Timeout<F> where - T: Future, + F: Future, { + let location = trace::caller_location(); + let deadline = Instant::now().checked_add(duration); let delay = match deadline { - Some(deadline) => Sleep::new_timeout(deadline), - None => Sleep::far_future(), + Some(deadline) => Sleep::new_timeout(deadline, location), + None => Sleep::far_future(location), }; Timeout::new_with_delay(future, delay) } -/// Require a `Future` to complete before the specified instant in time. +/// Requires a `Future` to complete before the specified instant in time. /// /// If the future completes before the instant is reached, then the completed /// value is returned. Otherwise, an error is returned. /// -/// # Cancelation +/// This function returns a future whose return type is [`Result`]`<T,`[`Elapsed`]`>`, where `T` is the +/// return type of the provided future. +/// +/// If the provided future completes immediately, then the future returned from +/// this function is guaranteed to complete immediately with an [`Ok`] variant +/// no matter the provided deadline. +/// +/// [`Ok`]: std::result::Result::Ok +/// [`Result`]: std::result::Result +/// [`Elapsed`]: crate::time::error::Elapsed +/// +/// # Cancellation /// /// Cancelling a timeout is done by dropping the future. No additional cleanup /// or other work is required. @@ -91,9 +142,9 @@ where /// } /// # } /// ``` -pub fn timeout_at<T>(deadline: Instant, future: T) -> Timeout<T> +pub fn timeout_at<F>(deadline: Instant, future: F) -> Timeout<F> where - T: Future, + F: Future, { let delay = sleep_until(deadline); @@ -145,15 +196,33 @@ where fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { let me = self.project(); + let had_budget_before = coop::has_budget_remaining(); + // First, try polling the future if let Poll::Ready(v) = me.value.poll(cx) { return Poll::Ready(Ok(v)); } - // Now check the timer - match me.delay.poll(cx) { - Poll::Ready(()) => Poll::Ready(Err(Elapsed::new())), - Poll::Pending => Poll::Pending, + let has_budget_now = coop::has_budget_remaining(); + + let delay = me.delay; + + let poll_delay = || -> Poll<Self::Output> { + match delay.poll(cx) { + Poll::Ready(()) => Poll::Ready(Err(Elapsed::new())), + Poll::Pending => Poll::Pending, + } + }; + + if let (true, false) = (had_budget_before, has_budget_now) { + // if it is the underlying future that exhausted the budget, we poll + // the `delay` with an unconstrained one. This prevents pathological + // cases where the underlying future always exhausts the budget and + // we never get a chance to evaluate whether the timeout was hit or + // not. + coop::with_unconstrained(poll_delay) + } else { + poll_delay() } } } |