use core::future::{Future, poll_fn};
use core::pin::Pin;
use core::task::{Context, Poll};
use std::time::Duration;
use super::Instant;
use super::sleep::{Sleep, far_future, sleep_until};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum MissedTickBehavior {
#[default]
Burst,
Delay,
Skip,
}
pub fn interval(period: Duration) -> Interval {
interval_at(Instant::now(), period)
}
pub fn interval_at(start: impl Into<Instant>, period: Duration) -> Interval {
let start = start.into();
assert!(
period > Duration::ZERO,
"an interval needs a period above zero"
);
Interval {
period,
sleep: sleep_until(start),
missed: MissedTickBehavior::default(),
}
}
#[derive(Debug)]
pub struct Interval {
period: Duration,
sleep: Sleep,
missed: MissedTickBehavior,
}
impl Interval {
pub async fn tick(&mut self) -> Instant {
poll_fn(|cx| self.poll_tick(cx)).await
}
pub fn poll_tick(&mut self, cx: &mut Context<'_>) -> Poll<Instant> {
if Pin::new(&mut self.sleep).poll(cx).is_pending() {
return Poll::Pending;
}
let due = self.sleep.deadline();
let now = Instant::now();
let next = match self.missed {
MissedTickBehavior::Burst => due.checked_add(self.period),
MissedTickBehavior::Delay => now.checked_add(self.period),
MissedTickBehavior::Skip => {
let late = now.saturating_duration_since(due).as_nanos();
let into = late % self.period.as_nanos();
let left = self.period - nanos_duration(into);
now.max(due).checked_add(left)
}
};
Pin::new(&mut self.sleep).reset(next.unwrap_or_else(far_future));
Poll::Ready(due)
}
pub fn reset(&mut self) {
let next = Instant::now().checked_add(self.period);
Pin::new(&mut self.sleep).reset(next.unwrap_or_else(far_future));
}
pub fn period(&self) -> Duration {
self.period
}
pub fn missed_tick_behavior(&self) -> MissedTickBehavior {
self.missed
}
pub fn set_missed_tick_behavior(&mut self, behavior: MissedTickBehavior) {
self.missed = behavior;
}
}
fn nanos_duration(nanos: u128) -> Duration {
const NANOS: u128 = 1_000_000_000;
let secs = u64::try_from(nanos / NANOS).unwrap_or(u64::MAX);
Duration::new(secs, (nanos % NANOS) as u32)
}