linera-kywasmtime 0.1.0

Time utils for WASM (Linera fork)
Documentation
use crate::sleep::Sleep;
use crate::time::Instant;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;

#[derive(Debug)]
pub struct Interval {
    sleep: Sleep,
    period: Duration,
    missed_tick_behavior: MissedTickBehavior,
}

impl Interval {
    fn new(start: Instant, period: Duration) -> Self {
        Self {
            sleep: Sleep::new(start),
            period,
            missed_tick_behavior: MissedTickBehavior::default(),
        }
    }

    pub async fn tick(&mut self) -> Instant {
        TickFuture(self).await
    }

    pub fn poll_tick(&mut self, cx: &mut Context<'_>) -> Poll<Instant> {
        let fut = Pin::new(&mut self.sleep);
        if fut.poll(cx).is_pending() {
            return Poll::Pending;
        }

        let timeout = self.sleep.deadline();
        let now = Instant::now();

        let next = self
            .missed_tick_behavior
            .next_timeout(timeout, now, self.period);
        Pin::new(&mut self.sleep).reset(next);

        return Poll::Ready(timeout);
    }

    pub fn reset(&mut self) {
        Pin::new(&mut self.sleep).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;
    }
}

struct TickFuture<'a>(&'a mut Interval);

impl<'a> Future for TickFuture<'a> {
    type Output = Instant;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        self.get_mut().0.poll_tick(cx)
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub enum MissedTickBehavior {
    #[default]
    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 => {
                let now_us = now.raw_us();
                let period_us: u64 = period.as_micros().try_into().unwrap();
                let timeout_us = timeout.raw_us();
                let next = now_us + period_us - ((now_us - timeout_us) % period_us);
                Instant::from_raw_us(next)
            }
        }
    }
}

pub fn interval_at(start: Instant, period: Duration) -> Interval {
    Interval::new(start, period)
}

pub fn interval(period: Duration) -> Interval {
    interval_at(Instant::now(), period)
}