use std::{
sync::atomic::{AtomicU64, Ordering},
time::{Instant, SystemTime, UNIX_EPOCH},
};
pub trait RuntimeObservationClock: Send + Sync + 'static {
fn monotonic_ns(&self) -> u64;
fn unix_epoch_ns(&self) -> Option<u64>;
}
#[derive(Debug)]
pub(crate) struct SystemRuntimeObservationClock {
origin: Instant,
unix_epoch_ns: Option<u64>,
}
impl SystemRuntimeObservationClock {
pub fn new() -> Self {
let unix_epoch_ns = SystemTime::now()
.duration_since(UNIX_EPOCH)
.ok()
.and_then(|duration| u64::try_from(duration.as_nanos()).ok());
Self { origin: Instant::now(), unix_epoch_ns }
}
}
impl Default for SystemRuntimeObservationClock {
fn default() -> Self {
Self::new()
}
}
impl RuntimeObservationClock for SystemRuntimeObservationClock {
fn monotonic_ns(&self) -> u64 {
u64::try_from(self.origin.elapsed().as_nanos()).unwrap_or(u64::MAX)
}
fn unix_epoch_ns(&self) -> Option<u64> {
self.unix_epoch_ns
}
}
#[derive(Debug)]
pub struct DeterministicRuntimeObservationClock {
monotonic_ns: AtomicU64,
unix_epoch_ns: Option<u64>,
}
impl DeterministicRuntimeObservationClock {
pub const fn new(unix_epoch_ns: Option<u64>) -> Self {
Self { monotonic_ns: AtomicU64::new(0), unix_epoch_ns }
}
pub fn set_monotonic_ns(&self, monotonic_ns: u64) {
self.monotonic_ns.store(monotonic_ns, Ordering::Release);
}
pub fn advance(&self, nanoseconds: u64) -> u64 {
self.monotonic_ns.fetch_add(nanoseconds, Ordering::AcqRel).saturating_add(nanoseconds)
}
}
impl RuntimeObservationClock for DeterministicRuntimeObservationClock {
fn monotonic_ns(&self) -> u64 {
self.monotonic_ns.load(Ordering::Acquire)
}
fn unix_epoch_ns(&self) -> Option<u64> {
self.unix_epoch_ns
}
}