time: implement extra reset variants for Interval (#5878)

This commit is contained in:
Victor Timofei
2023-08-04 19:12:51 +02:00
committed by GitHub
parent 7c54fdce3e
commit dbda2045f1
2 changed files with 251 additions and 0 deletions
+103
View File
@@ -495,6 +495,8 @@ impl Interval {
///
/// This method ignores [`MissedTickBehavior`] strategy.
///
/// This is equivalent to calling `reset_at(Instant::now() + period)`.
///
/// # Examples
///
/// ```
@@ -521,6 +523,107 @@ impl Interval {
self.delay.as_mut().reset(Instant::now() + self.period);
}
/// Resets the interval immediately.
///
/// This method ignores [`MissedTickBehavior`] strategy.
///
/// This is equivalent to calling `reset_at(Instant::now())`.
///
/// # Examples
///
/// ```
/// use tokio::time;
///
/// use std::time::Duration;
///
/// #[tokio::main]
/// async fn main() {
/// let mut interval = time::interval(Duration::from_millis(100));
///
/// interval.tick().await;
///
/// time::sleep(Duration::from_millis(50)).await;
/// interval.reset_immediately();
///
/// interval.tick().await;
/// interval.tick().await;
///
/// // approximately 150ms have elapsed.
/// }
/// ```
pub fn reset_immediately(&mut self) {
self.delay.as_mut().reset(Instant::now());
}
/// Resets the interval after the specified [`std::time::Duration`].
///
/// This method ignores [`MissedTickBehavior`] strategy.
///
/// This is equivalent to calling `reset_at(Instant::now() + after)`.
///
/// # Examples
///
/// ```
/// use tokio::time;
///
/// use std::time::Duration;
///
/// #[tokio::main]
/// async fn main() {
/// let mut interval = time::interval(Duration::from_millis(100));
/// interval.tick().await;
///
/// time::sleep(Duration::from_millis(50)).await;
///
/// let after = Duration::from_millis(20);
/// interval.reset_after(after);
///
/// interval.tick().await;
/// interval.tick().await;
///
/// // approximately 170ms have elapsed.
/// }
/// ```
pub fn reset_after(&mut self, after: Duration) {
self.delay.as_mut().reset(Instant::now() + after);
}
/// Resets the interval to a [`crate::time::Instant`] deadline.
///
/// Sets the next tick to expire at the given instant. If the instant is in
/// the past, then the [`MissedTickBehavior`] strategy will be used to
/// catch up. If the instant is in the future, then the next tick will
/// complete at the given instant, even if that means that it will sleep for
/// longer than the duration of this [`Interval`]. If the [`Interval`] had
/// any missed ticks before calling this method, then those are discarded.
///
/// # Examples
///
/// ```
/// use tokio::time::{self, Instant};
///
/// use std::time::Duration;
///
/// #[tokio::main]
/// async fn main() {
/// let mut interval = time::interval(Duration::from_millis(100));
/// interval.tick().await;
///
/// time::sleep(Duration::from_millis(50)).await;
///
/// let deadline = Instant::now() + Duration::from_millis(30);
/// interval.reset_at(deadline);
///
/// interval.tick().await;
/// interval.tick().await;
///
/// // approximately 180ms have elapsed.
/// }
/// ```
pub fn reset_at(&mut self, deadline: Instant) {
self.delay.as_mut().reset(deadline);
}
/// Returns the [`MissedTickBehavior`] strategy currently being used.
pub fn missed_tick_behavior(&self) -> MissedTickBehavior {
self.missed_tick_behavior
+148
View File
@@ -204,6 +204,154 @@ async fn reset() {
check_interval_poll!(i, start, 1001);
}
#[tokio::test(start_paused = true)]
async fn reset_immediatelly() {
let start = Instant::now();
// This is necessary because the timer is only so granular, and in order for
// all our ticks to resolve, the time needs to be 1ms ahead of what we
// expect, so that the runtime will see that it is time to resolve the timer
time::advance(ms(1)).await;
let mut i = task::spawn(time::interval_at(start, ms(300)));
check_interval_poll!(i, start, 0);
time::advance(ms(100)).await;
check_interval_poll!(i, start);
time::advance(ms(200)).await;
check_interval_poll!(i, start, 300);
time::advance(ms(100)).await;
check_interval_poll!(i, start);
i.reset_immediately();
// We add one because when using `reset` method, `Interval` adds the
// `period` from `Instant::now()`, which will always be off by one
check_interval_poll!(i, start, 401);
time::advance(ms(100)).await;
check_interval_poll!(i, start);
time::advance(ms(200)).await;
check_interval_poll!(i, start, 701);
}
#[tokio::test(start_paused = true)]
async fn reset_after() {
let start = Instant::now();
// This is necessary because the timer is only so granular, and in order for
// all our ticks to resolve, the time needs to be 1ms ahead of what we
// expect, so that the runtime will see that it is time to resolve the timer
time::advance(ms(1)).await;
let mut i = task::spawn(time::interval_at(start, ms(300)));
check_interval_poll!(i, start, 0);
time::advance(ms(100)).await;
check_interval_poll!(i, start);
time::advance(ms(200)).await;
check_interval_poll!(i, start, 300);
time::advance(ms(100)).await;
check_interval_poll!(i, start);
i.reset_after(Duration::from_millis(20));
// We add one because when using `reset` method, `Interval` adds the
// `period` from `Instant::now()`, which will always be off by one
time::advance(ms(20)).await;
check_interval_poll!(i, start, 421);
time::advance(ms(100)).await;
check_interval_poll!(i, start);
time::advance(ms(200)).await;
check_interval_poll!(i, start, 721);
}
#[tokio::test(start_paused = true)]
async fn reset_at() {
let start = Instant::now();
// This is necessary because the timer is only so granular, and in order for
// all our ticks to resolve, the time needs to be 1ms ahead of what we
// expect, so that the runtime will see that it is time to resolve the timer
time::advance(ms(1)).await;
let mut i = task::spawn(time::interval_at(start, ms(300)));
check_interval_poll!(i, start, 0);
time::advance(ms(100)).await;
check_interval_poll!(i, start);
time::advance(ms(200)).await;
check_interval_poll!(i, start, 300);
time::advance(ms(100)).await;
check_interval_poll!(i, start);
i.reset_at(Instant::now() + Duration::from_millis(40));
// We add one because when using `reset` method, `Interval` adds the
// `period` from `Instant::now()`, which will always be off by one
time::advance(ms(40)).await;
check_interval_poll!(i, start, 441);
time::advance(ms(100)).await;
check_interval_poll!(i, start);
time::advance(ms(200)).await;
check_interval_poll!(i, start, 741);
}
#[tokio::test(start_paused = true)]
async fn reset_at_bigger_than_interval() {
let start = Instant::now();
// This is necessary because the timer is only so granular, and in order for
// all our ticks to resolve, the time needs to be 1ms ahead of what we
// expect, so that the runtime will see that it is time to resolve the timer
time::advance(ms(1)).await;
let mut i = task::spawn(time::interval_at(start, ms(300)));
check_interval_poll!(i, start, 0);
time::advance(ms(100)).await;
check_interval_poll!(i, start);
time::advance(ms(200)).await;
check_interval_poll!(i, start, 300);
time::advance(ms(100)).await;
check_interval_poll!(i, start);
i.reset_at(Instant::now() + Duration::from_millis(1000));
// Validate the interval does not tick until 1000ms have passed
time::advance(ms(300)).await;
check_interval_poll!(i, start);
time::advance(ms(300)).await;
check_interval_poll!(i, start);
time::advance(ms(300)).await;
check_interval_poll!(i, start);
// We add one because when using `reset` method, `Interval` adds the
// `period` from `Instant::now()`, which will always be off by one
time::advance(ms(100)).await;
check_interval_poll!(i, start, 1401);
time::advance(ms(300)).await;
check_interval_poll!(i, start, 1701);
}
fn poll_next(interval: &mut task::Spawn<time::Interval>) -> Poll<Instant> {
interval.enter(|cx, mut interval| interval.poll_tick(cx))
}