use crate::store::platform::clock::Clock;
use std::sync::atomic::{AtomicI64, Ordering};
const EPOCH_US: i64 = 2_000_000_000_000_000;
#[derive(Debug)]
pub struct SimClock {
now_us: AtomicI64,
mono_ns: AtomicI64,
}
impl SimClock {
#[must_use]
pub fn new() -> Self {
Self {
now_us: AtomicI64::new(EPOCH_US),
mono_ns: AtomicI64::new(0),
}
}
pub fn advance_us(&self, delta_us: i64) -> i64 {
let delta = delta_us.max(0);
self.mono_ns
.fetch_add(delta.saturating_mul(1000), Ordering::AcqRel);
self.now_us.fetch_add(delta, Ordering::AcqRel) + delta
}
}
impl Default for SimClock {
fn default() -> Self {
Self::new()
}
}
impl Clock for SimClock {
fn now_us(&self) -> i64 {
self.now_us.load(Ordering::Acquire)
}
fn now_wall_ns(&self) -> i64 {
self.now_us.load(Ordering::Acquire).saturating_mul(1000)
}
fn now_mono_ns(&self) -> i64 {
self.mono_ns.load(Ordering::Acquire)
}
fn process_boot_ns(&self) -> u64 {
0x53_49_4D_43_4C_4F_43_4B
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clock_starts_at_epoch_and_only_advances() {
let c = SimClock::new();
assert_eq!(c.now_us(), EPOCH_US, "PROPERTY: clock parks at fixed epoch");
let after = c.advance_us(250);
assert_eq!(
after,
EPOCH_US + 250,
"PROPERTY: advance returns new now_us"
);
assert_eq!(c.now_us(), EPOCH_US + 250);
let held = c.advance_us(-1000);
assert_eq!(held, EPOCH_US + 250, "PROPERTY: clock never regresses");
}
#[test]
fn process_boot_ns_is_the_fixed_simclock_marker() {
let c = SimClock::new();
assert_eq!(
c.process_boot_ns(),
0x53_49_4D_43_4C_4F_43_4B,
"SimClock::process_boot_ns must return the fixed SIMCLOCK marker, not 1"
);
}
#[test]
fn monotonic_stream_tracks_microseconds() {
let c = SimClock::new();
c.advance_us(3);
assert_eq!(
c.now_mono_ns(),
3000,
"PROPERTY: logical monotonic ns tracks logical microseconds"
);
assert_eq!(c.now_wall_ns(), c.now_us() * 1000);
}
}