use std::{
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
time::Duration,
};
pub trait Clock: Send + Sync {
fn now_nanos(&self) -> u64;
fn to_absolute_nanos(&self, _now_nanos: u64) -> Option<u64> {
None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Tick {
pub index: u64,
pub clock_nanos: u64,
pub absolute_nanos: Option<u64>,
}
#[must_use = "dropping the TickGuard immediately stops tick delivery"]
pub struct TickGuard {
alive: Arc<AtomicBool>,
}
impl TickGuard {
pub const fn leak(self) {
std::mem::forget(self);
}
}
impl Drop for TickGuard {
fn drop(&mut self) {
self.alive.store(false, Ordering::Relaxed);
}
}
pub trait TickSource: Send + Sync {
fn on_tick(&self, f: Box<dyn FnMut(Tick) + Send>) -> TickGuard;
}
#[derive(Debug, Clone, Default)]
pub struct MonotonicClock {
_private: (),
}
impl MonotonicClock {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
impl Clock for MonotonicClock {
fn now_nanos(&self) -> u64 {
crate::platform::now_nanos()
}
}
pub struct IntervalTickSource {
period: Duration,
precise: bool,
clock: Arc<dyn Clock>,
}
impl IntervalTickSource {
#[must_use]
pub fn new(period: Duration) -> Self {
Self {
period,
precise: false,
clock: Arc::new(MonotonicClock::new()),
}
}
#[must_use]
pub const fn precise(mut self) -> Self {
self.precise = true;
self
}
#[must_use]
pub fn with_clock(mut self, clock: Arc<dyn Clock>) -> Self {
self.clock = clock;
self
}
}
impl TickSource for IntervalTickSource {
fn on_tick(&self, mut f: Box<dyn FnMut(Tick) + Send>) -> TickGuard {
let alive = Arc::new(AtomicBool::new(true));
let alive_timer = alive.clone();
let clock = self.clock.clone();
crate::platform::spawn_interval(self.period, self.precise, move |count| {
if !alive_timer.load(Ordering::Relaxed) {
return false;
}
let clock_nanos = clock.now_nanos();
f(Tick {
index: count,
clock_nanos,
absolute_nanos: clock.to_absolute_nanos(clock_nanos),
});
true
});
TickGuard { alive }
}
}