mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-07 00:00:08 +02:00
time: do not panic on timeout(Duration::MAX) (#3551)
It is tempting to use very large `Duration` value to get a practically infinite timeout. Before this commit Tokio panics on checked Instant + Duration overflow. This commit implements very simple fix: if Instant + Duration overflows, we use duration = 30 years. Better fix should avoid firing a timer on duration overflow. It requires deeper understanding how timers work, but also it is not clear, for example, what `Sleep::deadline` function should return. Similar fix is done for `sleep`.
This commit is contained in:
@@ -58,7 +58,10 @@ pub fn sleep_until(deadline: Instant) -> Sleep {
|
|||||||
// Alias for old name in 0.x
|
// Alias for old name in 0.x
|
||||||
#[cfg_attr(docsrs, doc(alias = "delay_for"))]
|
#[cfg_attr(docsrs, doc(alias = "delay_for"))]
|
||||||
pub fn sleep(duration: Duration) -> Sleep {
|
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! {
|
pin_project! {
|
||||||
@@ -168,6 +171,10 @@ impl Sleep {
|
|||||||
Sleep { deadline, entry }
|
Sleep { deadline, entry }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn far_future() -> Sleep {
|
||||||
|
Self::new_timeout(Instant::far_future())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the instant at which the future will complete.
|
/// Returns the instant at which the future will complete.
|
||||||
pub fn deadline(&self) -> Instant {
|
pub fn deadline(&self) -> Instant {
|
||||||
self.deadline
|
self.deadline
|
||||||
|
|||||||
@@ -54,6 +54,14 @@ impl Instant {
|
|||||||
Instant { std }
|
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`.
|
/// Convert the value into a `std::time::Instant`.
|
||||||
pub fn into_std(self) -> std::time::Instant {
|
pub fn into_std(self) -> std::time::Instant {
|
||||||
self.std
|
self.std
|
||||||
|
|||||||
@@ -49,7 +49,11 @@ pub fn timeout<T>(duration: Duration, future: T) -> Timeout<T>
|
|||||||
where
|
where
|
||||||
T: Future,
|
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)
|
Timeout::new_with_delay(future, delay)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,33 @@ async fn future_and_timeout_in_future() {
|
|||||||
assert_ready_ok!(fut.poll()).unwrap();
|
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]
|
#[tokio::test]
|
||||||
async fn deadline_now_elapses() {
|
async fn deadline_now_elapses() {
|
||||||
use futures::future::pending;
|
use futures::future::pending;
|
||||||
|
|||||||
Reference in New Issue
Block a user