use std::time::{Duration, Instant, SystemTime};
use crate::time::MonotonicTime;
pub trait Clock: Send {
fn synchronize(&mut self, deadline: MonotonicTime) -> SyncStatus;
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum SyncStatus {
Synchronized,
OutOfSync(Duration),
}
#[derive(Copy, Clone, Debug, Default)]
pub struct NoClock {}
impl NoClock {
pub fn new() -> Self {
Self {}
}
}
impl Clock for NoClock {
fn synchronize(&mut self, _: MonotonicTime) -> SyncStatus {
SyncStatus::Synchronized
}
}
#[derive(Copy, Clone, Debug)]
pub struct SystemClock {
wall_clock_ref: Instant,
simulation_ref: MonotonicTime,
}
impl SystemClock {
pub fn from_instant(simulation_ref: MonotonicTime, wall_clock_ref: Instant) -> Self {
Self {
wall_clock_ref,
simulation_ref,
}
}
pub fn from_system_time(simulation_ref: MonotonicTime, wall_clock_ref: SystemTime) -> Self {
const SAMPLES: usize = 3;
let mut last_instant = Instant::now();
let mut min_delta = Duration::MAX;
let mut ref_time = None;
for _ in 0..SAMPLES {
let (date, instant, delta) = loop {
let date = SystemTime::now();
let instant = Instant::now();
let delta = instant.checked_duration_since(last_instant);
last_instant = instant;
if let Some(delta) = delta {
break (date, instant, delta);
}
};
if min_delta > delta {
min_delta = delta;
ref_time = Some((instant, date));
}
}
let (instant_ref, date_ref) = ref_time.unwrap();
let simulation_ref = if date_ref > wall_clock_ref {
let correction = date_ref.duration_since(wall_clock_ref).unwrap();
simulation_ref + correction
} else {
let correction = wall_clock_ref.duration_since(date_ref).unwrap();
simulation_ref - correction
};
Self {
wall_clock_ref: instant_ref,
simulation_ref,
}
}
}
impl Clock for SystemClock {
fn synchronize(&mut self, deadline: MonotonicTime) -> SyncStatus {
let target_time = if deadline >= self.simulation_ref {
self.wall_clock_ref + deadline.duration_since(self.simulation_ref)
} else {
self.wall_clock_ref - self.simulation_ref.duration_since(deadline)
};
let now = Instant::now();
match target_time.checked_duration_since(now) {
Some(sleep_duration) => {
spin_sleep::sleep(sleep_duration);
SyncStatus::Synchronized
}
None => SyncStatus::OutOfSync(now.duration_since(target_time)),
}
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct AutoSystemClock {
inner: Option<SystemClock>,
}
impl AutoSystemClock {
pub fn new() -> Self {
Self::default()
}
}
impl Clock for AutoSystemClock {
fn synchronize(&mut self, deadline: MonotonicTime) -> SyncStatus {
match &mut self.inner {
None => {
let now = Instant::now();
self.inner = Some(SystemClock::from_instant(deadline, now));
SyncStatus::Synchronized
}
Some(clock) => clock.synchronize(deadline),
}
}
}