use crate::time::{sleep_until, Duration, Instant, Sleep};
use futures_util::future::poll_fn;
use futures_util::ready;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
pub fn interval(period: Duration) -> Interval {
assert!(period > Duration::new(0, 0), "`period` must be non-zero.");
internal_interval_at(Instant::now(), period)
}
pub fn interval_at(start: Instant, period: Duration) -> Interval {
assert!(period > Duration::new(0, 0), "`period` must be non-zero.");
internal_interval_at(start, period)
}
fn internal_interval_at(start: Instant, period: Duration) -> Interval {
let delay = Box::pin(sleep_until(start));
Interval {
delay,
period,
missed_tick_behavior: Default::default(),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MissedTickBehavior {
Burst,
Delay,
Skip,
}
impl MissedTickBehavior {
fn next_timeout(&self, timeout: Instant, now: Instant, period: Duration) -> Instant {
match self {
Self::Burst => timeout + period,
Self::Delay => now + period,
Self::Skip => {
now + period
.checked_sub(Duration::from_nanos(
((now - timeout).as_nanos() % period.as_nanos())
.try_into()
.expect(
"too much time has elapsed since the interval was supposed to tick",
),
))
.unwrap()
}
}
}
}
impl Default for MissedTickBehavior {
fn default() -> Self {
Self::Burst
}
}
#[derive(Debug)]
pub struct Interval {
delay: Pin<Box<Sleep>>,
period: Duration,
missed_tick_behavior: MissedTickBehavior,
}
impl Interval {
pub async fn tick(&mut self) -> Instant {
let instant = poll_fn(|cx| self.poll_tick(cx));
instant.await
}
pub fn poll_tick(&mut self, cx: &mut Context<'_>) -> Poll<Instant> {
ready!(Pin::new(&mut self.delay).poll(cx));
let timeout = self.delay.deadline();
let now = Instant::now();
let next = if now > timeout + Duration::from_millis(5) {
self.missed_tick_behavior
.next_timeout(timeout, now, self.period)
} else {
timeout + self.period
};
self.delay.as_mut().reset(next);
Poll::Ready(timeout)
}
pub fn reset(&mut self) {
self.delay.as_mut().reset(Instant::now() + self.period);
}
pub fn missed_tick_behavior(&self) -> MissedTickBehavior {
self.missed_tick_behavior
}
pub fn set_missed_tick_behavior(&mut self, behavior: MissedTickBehavior) {
self.missed_tick_behavior = behavior;
}
pub fn period(&self) -> Duration {
self.period
}
}