Skip to content

Commit 2172e0f

Browse files
implement extra reset variants for Interval
Fixes: #5874
1 parent e5e8855 commit 2172e0f

File tree

2 files changed

+210
-0
lines changed

2 files changed

+210
-0
lines changed

tokio/src/time/interval.rs

+103
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,8 @@ impl Interval {
495495
///
496496
/// This method ignores [`MissedTickBehavior`] strategy.
497497
///
498+
/// This is equivalent to calling `reset_at(Instant::now() + period)`.
499+
///
498500
/// # Examples
499501
///
500502
/// ```
@@ -521,6 +523,107 @@ impl Interval {
521523
self.delay.as_mut().reset(Instant::now() + self.period);
522524
}
523525

526+
/// Resets the interval immediately.
527+
///
528+
/// This method ignores [`MissedTickBehavior`] strategy.
529+
///
530+
/// This is equivalent to calling `reset_at(Instant::now())`.
531+
///
532+
/// # Examples
533+
///
534+
/// ```
535+
/// use tokio::time;
536+
///
537+
/// use std::time::Duration;
538+
///
539+
/// #[tokio::main]
540+
/// async fn main() {
541+
/// let mut interval = time::interval(Duration::from_millis(100));
542+
///
543+
/// interval.tick().await;
544+
///
545+
/// time::sleep(Duration::from_millis(50)).await;
546+
/// interval.reset_immediately();
547+
///
548+
/// interval.tick().await;
549+
/// interval.tick().await;
550+
///
551+
/// // approximately 150ms have elapsed.
552+
/// }
553+
/// ```
554+
pub fn reset_immediately(&mut self) {
555+
self.delay.as_mut().reset(Instant::now());
556+
}
557+
558+
/// Resets the interval after the specified [`std::time::Duration`].
559+
///
560+
/// This method ignores [`MissedTickBehavior`] strategy.
561+
///
562+
/// This is equivalent to calling `reset_at(Instant::now() + after)`.
563+
///
564+
/// # Examples
565+
///
566+
/// ```
567+
/// use tokio::time;
568+
///
569+
/// use std::time::Duration;
570+
///
571+
/// #[tokio::main]
572+
/// async fn main() {
573+
/// let mut interval = time::interval(Duration::from_millis(100));
574+
/// interval.tick().await;
575+
///
576+
/// time::sleep(Duration::from_millis(50)).await;
577+
///
578+
/// let after = Duration::from_millis(20);
579+
/// interval.reset_after(after);
580+
///
581+
/// interval.tick().await;
582+
/// interval.tick().await;
583+
///
584+
/// // approximately 170ms have elapsed.
585+
/// }
586+
/// ```
587+
pub fn reset_after(&mut self, after: Duration) {
588+
self.delay.as_mut().reset(Instant::now() + after);
589+
}
590+
591+
/// Resets the interval to a [`crate::time::Instant`] deadline.
592+
///
593+
/// Sets the next tick to expire at the given instant. If the instant is in
594+
/// the past, then the [`MissedTickBehavior`] strategy will be used to
595+
/// catch up. If the instant is in the future, then the next tick will
596+
/// complete at the given instant, even if that means that it will sleep for
597+
/// longer than the duration of this [`Interval`]. If the [`Interval`] had
598+
/// any missed ticks before calling this method, then those are discarded.
599+
///
600+
/// # Examples
601+
///
602+
/// ```
603+
/// use tokio::time::{self, Instant};
604+
///
605+
/// use std::time::Duration;
606+
///
607+
/// #[tokio::main]
608+
/// async fn main() {
609+
/// let mut interval = time::interval(Duration::from_millis(100));
610+
/// interval.tick().await;
611+
///
612+
/// time::sleep(Duration::from_millis(50)).await;
613+
///
614+
/// let deadline = Instant::now() + Duration::from_millis(30);
615+
/// interval.reset_at(deadline);
616+
///
617+
/// interval.tick().await;
618+
/// interval.tick().await;
619+
///
620+
/// // approximately 180ms have elapsed.
621+
/// }
622+
/// ```
623+
pub fn reset_at(&mut self, deadline: Instant) {
624+
self.delay.as_mut().reset(deadline);
625+
}
626+
524627
/// Returns the [`MissedTickBehavior`] strategy currently being used.
525628
pub fn missed_tick_behavior(&self) -> MissedTickBehavior {
526629
self.missed_tick_behavior

tokio/tests/time_interval.rs

+107
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,113 @@ async fn reset() {
204204
check_interval_poll!(i, start, 1001);
205205
}
206206

207+
#[tokio::test(start_paused = true)]
208+
async fn reset_immediatelly() {
209+
let start = Instant::now();
210+
211+
// This is necessary because the timer is only so granular, and in order for
212+
// all our ticks to resolve, the time needs to be 1ms ahead of what we
213+
// expect, so that the runtime will see that it is time to resolve the timer
214+
time::advance(ms(1)).await;
215+
216+
let mut i = task::spawn(time::interval_at(start, ms(300)));
217+
218+
check_interval_poll!(i, start, 0);
219+
220+
time::advance(ms(100)).await;
221+
check_interval_poll!(i, start);
222+
223+
time::advance(ms(200)).await;
224+
check_interval_poll!(i, start, 300);
225+
226+
time::advance(ms(100)).await;
227+
check_interval_poll!(i, start);
228+
229+
i.reset_immediately();
230+
231+
// We add one because when using `reset` method, `Interval` adds the
232+
// `period` from `Instant::now()`, which will always be off by one
233+
check_interval_poll!(i, start, 401);
234+
235+
time::advance(ms(100)).await;
236+
check_interval_poll!(i, start);
237+
238+
time::advance(ms(200)).await;
239+
check_interval_poll!(i, start, 701);
240+
}
241+
242+
#[tokio::test(start_paused = true)]
243+
async fn reset_after() {
244+
let start = Instant::now();
245+
246+
// This is necessary because the timer is only so granular, and in order for
247+
// all our ticks to resolve, the time needs to be 1ms ahead of what we
248+
// expect, so that the runtime will see that it is time to resolve the timer
249+
time::advance(ms(1)).await;
250+
251+
let mut i = task::spawn(time::interval_at(start, ms(300)));
252+
253+
check_interval_poll!(i, start, 0);
254+
255+
time::advance(ms(100)).await;
256+
check_interval_poll!(i, start);
257+
258+
time::advance(ms(200)).await;
259+
check_interval_poll!(i, start, 300);
260+
261+
time::advance(ms(100)).await;
262+
check_interval_poll!(i, start);
263+
264+
i.reset_after(Duration::from_millis(20));
265+
266+
// We add one because when using `reset` method, `Interval` adds the
267+
// `period` from `Instant::now()`, which will always be off by one
268+
time::advance(ms(20)).await;
269+
check_interval_poll!(i, start, 421);
270+
271+
time::advance(ms(100)).await;
272+
check_interval_poll!(i, start);
273+
274+
time::advance(ms(200)).await;
275+
check_interval_poll!(i, start, 721);
276+
}
277+
278+
#[tokio::test(start_paused = true)]
279+
async fn reset_at() {
280+
let start = Instant::now();
281+
282+
// This is necessary because the timer is only so granular, and in order for
283+
// all our ticks to resolve, the time needs to be 1ms ahead of what we
284+
// expect, so that the runtime will see that it is time to resolve the timer
285+
time::advance(ms(1)).await;
286+
287+
let mut i = task::spawn(time::interval_at(start, ms(300)));
288+
289+
check_interval_poll!(i, start, 0);
290+
291+
time::advance(ms(100)).await;
292+
check_interval_poll!(i, start);
293+
294+
time::advance(ms(200)).await;
295+
check_interval_poll!(i, start, 300);
296+
297+
time::advance(ms(100)).await;
298+
check_interval_poll!(i, start);
299+
300+
i.reset_at(Instant::now() + Duration::from_millis(40));
301+
302+
// We add one because when using `reset` method, `Interval` adds the
303+
// `period` from `Instant::now()`, which will always be off by one
304+
time::advance(ms(40)).await;
305+
check_interval_poll!(i, start, 441);
306+
307+
time::advance(ms(100)).await;
308+
check_interval_poll!(i, start);
309+
310+
time::advance(ms(200)).await;
311+
check_interval_poll!(i, start, 741);
312+
}
313+
207314
fn poll_next(interval: &mut task::Spawn<time::Interval>) -> Poll<Instant> {
208315
interval.enter(|cx, mut interval| interval.poll_tick(cx))
209316
}

0 commit comments

Comments
 (0)