//! The engine's clock — every timestamp the mem engine produces (unix //! seconds stamps, visibility deadlines, TTLs, scheduler boundaries) //! reads this one accessor ([`Clock::unix_now`]); the process clock is //! all there is (engine-mem.md: no server clock to defer to). //! //! The single point exists so tests can observe timestamps //! deterministically the way the SQL engines observe row shapes: a //! `#[cfg(test)]` freeze/advance seam pins a wall value and every //! accessor call returns it (see the unit pins below). Production //! reads `std::time::SystemTime`. #[cfg(test)] use std::sync::Mutex; #[cfg(test)] use crate::state::Guard; // Mechanism task code stamps every timestamp through this accessor — // scaffolding reads as dead until the wave-6 mechanism tasks wire it. #[allow(dead_code)] fn process_unix_now() -> i64 { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .map(|since| since.as_secs() as i64) .unwrap_or(0) } /// The engine's single clock. #[derive(Debug)] pub(crate) struct Clock { #[cfg(test)] frozen: Mutex>, } impl Default for Clock { fn default() -> Self { Self::new() } } impl Clock { pub(crate) fn new() -> Self { Self { #[cfg(test)] frozen: Mutex::new(None), } } /// Unix-seconds "now" — the one timestamp source of the engine. #[allow(dead_code)] pub(crate) fn unix_now(&self) -> i64 { #[cfg(test)] let frozen = { *self.frozen.guard() }; #[cfg(test)] if let Some(unix) = frozen { return unix; } process_unix_now() } #[cfg(test)] pub(crate) fn freeze(&self, at_unix: i64) { *self.frozen.guard() = Some(at_unix); } #[cfg(test)] pub(crate) fn advance(&self, seconds: i64) { let mut guard = self.frozen.guard(); *guard = Some(frozen_now(&guard).saturating_add(seconds)); } #[cfg(test)] pub(crate) fn unfreeze(&self) { *self.frozen.guard() = None; } } #[cfg(test)] fn frozen_now(frozen: &Option) -> i64 { frozen.unwrap_or_else(process_unix_now) } #[cfg(test)] mod tests { use super::*; #[test] fn unfrozen_reads_a_plausible_process_time() { let clock = Clock::new(); let now = clock.unix_now(); assert!( now > 1_700_000_000, "the accessor reads the process clock when unfrozen" ); } #[test] fn frozen_reads_deterministically_and_advances() { let clock = Clock::new(); clock.freeze(1_234_567); assert_eq!(clock.unix_now(), 1_234_567); clock.advance(10); assert_eq!(clock.unix_now(), 1_234_577); clock.unfreeze(); assert!( clock.unix_now() > 1_700_000_000, "unfreeze restores the process clock" ); } }