Skip to content

Commit

Permalink
time: fix time::advance() with sub-ms durations
Browse files Browse the repository at this point in the history
Update the advance logic to factor in the timer's ms rounding.

Fixes #3837
  • Loading branch information
carllerche committed Jun 11, 2021
1 parent 1baea39 commit 98cd6d4
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 1 deletion.
12 changes: 11 additions & 1 deletion tokio/src/time/clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,17 @@ cfg_test_util! {
let until = clock.now() + duration;
clock.advance(duration);

crate::time::sleep_until(until).await;
// Fixes tokio-rs/tokio#3837
//
// "sleep_until" is needed to ensure registered timers are fired after the
// advance call, however, it will also round up time to the nearest ms. To
// avoid this loss of precision, we sleep until the previous millisecond.
// If the advance is less than 1ms, then we just need to yield.
if duration >= Duration::from_millis(1) {
crate::time::sleep_until(until - Duration::from_millis(1)).await;
} else {
crate::task::yield_now().await;
}
}

/// Return the current instant, factoring in frozen time.
Expand Down
34 changes: 34 additions & 0 deletions tokio/tests/time_pause.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,40 @@ async fn interval() {
assert_pending!(poll_next(&mut i));
}

#[tokio::test]
async fn test_time_advance_sub_ms() {
time::pause();
let now = Instant::now();

let dur = Duration::from_micros(51_592);
time::advance(dur).await;

assert_eq!(now.elapsed(), dur);

let now = Instant::now();
let dur = Duration::from_micros(1);
time::advance(dur).await;

assert_eq!(now.elapsed(), dur);
}

#[tokio::test]
async fn test_time_advance_3ms_and_change() {
time::pause();
let now = Instant::now();

let dur = Duration::from_micros(3_141_592);
time::advance(dur).await;

assert_eq!(now.elapsed(), dur);

let now = Instant::now();
let dur = Duration::from_micros(3_123_456);
time::advance(dur).await;

assert_eq!(now.elapsed(), dur);
}

fn poll_next(interval: &mut task::Spawn<time::Interval>) -> Poll<Instant> {
interval.enter(|cx, mut interval| interval.poll_tick(cx))
}
Expand Down

0 comments on commit 98cd6d4

Please sign in to comment.