diff --git a/tokio/src/time/clock.rs b/tokio/src/time/clock.rs index 83b87cac1b0..93c4f9c49d2 100644 --- a/tokio/src/time/clock.rs +++ b/tokio/src/time/clock.rs @@ -285,13 +285,19 @@ cfg_test_util! { } pub(crate) fn advance(&self, duration: Duration) -> Result<(), &'static str> { + // Retrieve `far_future` before acquiring the mutex to prevent deadlock, + // as `Instant::far_future()` also acquires a mutex internally. + let far_future = Instant::far_future().into_std(); let mut inner = self.inner.lock(); if inner.unfrozen.is_some() { return Err("time is not frozen"); } - inner.base += duration; + inner.base = inner + .base + .checked_add(duration) + .unwrap_or(far_future); Ok(()) } diff --git a/tokio/tests/time_pause.rs b/tokio/tests/time_pause.rs index be993b4c8dd..aee74282981 100644 --- a/tokio/tests/time_pause.rs +++ b/tokio/tests/time_pause.rs @@ -288,6 +288,58 @@ async fn exact_1ms_advance() { assert_eq!(now.elapsed(), dur); } +#[tokio::test(start_paused = true)] +async fn advance_duration_max() { + let now = Instant::now(); + + let dur = Duration::from_millis(1); + time::advance(dur).await; + assert_eq!(now.elapsed(), dur); + + let now = Instant::now(); + time::advance(Duration::MAX).await; + // magic number from `tokio::time::Instant::far_future()` + assert_eq!(now.elapsed(), Duration::from_secs(86400 * 365 * 30)); + + // then we need to test if the timer wheel is still working correctly + + // hit the first level of wheel + let mut sleep = task::spawn(time::sleep(Duration::from_millis(1))); + assert_pending!(sleep.poll()); + time::advance(Duration::from_millis(1)).await; + assert!(sleep.is_woken()); + + // hit the second level of wheel + let mut sleep = task::spawn(time::sleep(Duration::from_millis(65))); + assert_pending!(sleep.poll()); + time::advance(Duration::from_millis(65)).await; + assert!(sleep.is_woken()); + + // hit the third level of wheel + let mut sleep = task::spawn(time::sleep(Duration::from_secs(5))); + assert_pending!(sleep.poll()); + time::advance(Duration::from_secs(5)).await; + assert!(sleep.is_woken()); + + // hit the fourth level of wheel + let mut sleep = task::spawn(time::sleep(Duration::from_secs(60 * 5))); + assert_pending!(sleep.poll()); + time::advance(Duration::from_secs(60 * 5)).await; + assert!(sleep.is_woken()); + + // hit the fifth level of wheel + let mut sleep = task::spawn(time::sleep(Duration::from_secs(60 * 60 * 5))); + assert_pending!(sleep.poll()); + time::advance(Duration::from_secs(60 * 60 * 5)).await; + assert!(sleep.is_woken()); + + // hit the sixth level of wheel + let mut sleep = task::spawn(time::sleep(Duration::from_secs(60 * 60 * 24 * 13))); + assert_pending!(sleep.poll()); + time::advance(Duration::from_secs(60 * 60 * 24 * 13)).await; + assert!(sleep.is_woken()); +} + #[tokio::test(start_paused = true)] async fn advance_once_with_timer() { let mut sleep = task::spawn(time::sleep(Duration::from_millis(1)));