use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
use std::time::Duration;
pub trait Clock: Clone + Send + Sync + 'static {
fn now(&self) -> Duration;
}
#[derive(Clone, Debug)]
pub struct SystemClock {
clock: quanta::Clock,
origin: quanta::Instant,
}
impl SystemClock {
pub fn new() -> Self {
let clock = quanta::Clock::new();
let origin = clock.now();
Self { clock, origin }
}
}
impl Default for SystemClock {
fn default() -> Self {
Self::new()
}
}
impl Clock for SystemClock {
#[inline]
fn now(&self) -> Duration {
self.clock.now().duration_since(self.origin)
}
}
#[derive(Clone, Debug, Default)]
pub struct ManualClock(Arc<AtomicU64>);
impl ManualClock {
pub fn new() -> Self {
Self::default()
}
pub fn advance(&self, by: Duration) {
self.0.fetch_add(by.as_nanos() as u64, Relaxed);
}
pub fn set(&self, to: Duration) {
self.0.store(to.as_nanos() as u64, Relaxed);
}
}
impl Clock for ManualClock {
#[inline]
fn now(&self) -> Duration {
Duration::from_nanos(self.0.load(Relaxed))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_manual_clock_shares_one_timeline_across_clones() {
let clock = ManualClock::new();
let handle = clock.clone();
assert_eq!(handle.now(), Duration::ZERO);
clock.advance(Duration::from_millis(250));
assert_eq!(handle.now(), Duration::from_millis(250));
clock.set(Duration::from_secs(9));
assert_eq!(handle.now(), Duration::from_secs(9));
}
#[test]
fn clones_of_a_system_clock_share_one_origin() {
let clock = SystemClock::new();
let handle = clock.clone();
let (first, second) = (clock.now(), handle.now());
let apart = second.saturating_sub(first);
assert!(
apart < Duration::from_millis(1),
"clones disagreed by {apart:?}, so they are not reading one timeline"
);
}
#[test]
fn the_system_clock_is_monotonic_and_actually_advances_from_its_origin() {
let clock = SystemClock::default();
let first = clock.now();
let second = clock.now();
assert!(second >= first, "a clock that went backwards would break every deadline");
let start = std::time::Instant::now();
while clock.now() == Duration::ZERO {
assert!(
start.elapsed() < Duration::from_secs(5),
"the system clock never left its origin"
);
std::hint::spin_loop();
}
}
}