diff --git a/tokio/src/time/driver/sleep.rs b/tokio/src/time/driver/sleep.rs index d8173c2524b..5c366293d5f 100644 --- a/tokio/src/time/driver/sleep.rs +++ b/tokio/src/time/driver/sleep.rs @@ -58,7 +58,10 @@ pub fn sleep_until(deadline: Instant) -> Sleep { // Alias for old name in 0.x #[cfg_attr(docsrs, doc(alias = "delay_for"))] pub fn sleep(duration: Duration) -> Sleep { - sleep_until(Instant::now() + duration) + match Instant::now().checked_add(duration) { + Some(deadline) => sleep_until(deadline), + None => sleep_until(Instant::far_future()), + } } pin_project! { @@ -168,6 +171,10 @@ impl Sleep { Sleep { deadline, entry } } + pub(crate) fn far_future() -> Sleep { + Self::new_timeout(Instant::far_future()) + } + /// Returns the instant at which the future will complete. pub fn deadline(&self) -> Instant { self.deadline diff --git a/tokio/src/time/instant.rs b/tokio/src/time/instant.rs index f4d6eacbfa5..1f8e663fe10 100644 --- a/tokio/src/time/instant.rs +++ b/tokio/src/time/instant.rs @@ -54,6 +54,14 @@ impl Instant { Instant { std } } + pub(crate) fn far_future() -> Instant { + // Roughly 30 years from now. + // API does not provide a way to obtain max `Instant` + // or convert specific date in the future to instant. + // 1000 years overflows on macOS, 100 years overflows on FreeBSD. + Self::now() + Duration::from_secs(86400 * 365 * 30) + } + /// Convert the value into a `std::time::Instant`. pub fn into_std(self) -> std::time::Instant { self.std diff --git a/tokio/src/time/timeout.rs b/tokio/src/time/timeout.rs index 9d15a7205cd..61964ad2426 100644 --- a/tokio/src/time/timeout.rs +++ b/tokio/src/time/timeout.rs @@ -49,7 +49,11 @@ pub fn timeout(duration: Duration, future: T) -> Timeout where T: Future, { - let delay = Sleep::new_timeout(Instant::now() + duration); + let deadline = Instant::now().checked_add(duration); + let delay = match deadline { + Some(deadline) => Sleep::new_timeout(deadline), + None => Sleep::far_future(), + }; Timeout::new_with_delay(future, delay) } diff --git a/tokio/tests/time_timeout.rs b/tokio/tests/time_timeout.rs index 4efcd8ca82f..dbd80eb8a6a 100644 --- a/tokio/tests/time_timeout.rs +++ b/tokio/tests/time_timeout.rs @@ -74,6 +74,33 @@ async fn future_and_timeout_in_future() { assert_ready_ok!(fut.poll()).unwrap(); } +#[tokio::test] +async fn very_large_timeout() { + time::pause(); + + // Not yet complete + let (tx, rx) = oneshot::channel(); + + // copy-paste unstable `Duration::MAX` + let duration_max = Duration::from_secs(u64::MAX) + Duration::from_nanos(999_999_999); + + // Wrap it with a deadline + let mut fut = task::spawn(timeout(duration_max, rx)); + + // Ready! + assert_pending!(fut.poll()); + + // Turn the timer, it runs for the elapsed time + time::advance(Duration::from_secs(86400 * 365 * 10)).await; + + assert_pending!(fut.poll()); + + // Complete the future + tx.send(()).unwrap(); + + assert_ready_ok!(fut.poll()).unwrap(); +} + #[tokio::test] async fn deadline_now_elapses() { use futures::future::pending;