use core::fmt;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
pub struct LogicalTime {
pub run_id: u64,
pub sequence: u64,
}
impl LogicalTime {
#[must_use]
pub const fn new(run_id: u64, sequence: u64) -> Self {
Self { run_id, sequence }
}
#[must_use]
pub const fn initial(run_id: u64) -> Self {
Self { run_id, sequence: 0 }
}
#[must_use]
pub const fn next(&self) -> Self {
Self {
run_id: self.run_id,
sequence: self.sequence + 1,
}
}
#[must_use]
pub const fn run_id(&self) -> u64 {
self.run_id
}
#[must_use]
pub const fn sequence(&self) -> u64 {
self.sequence
}
}
impl fmt::Display for LogicalTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "T({}:{})", self.run_id, self.sequence)
}
}
pub trait TimeSource: Send + Sync {
fn now(&self) -> LogicalTime;
fn tick(&self) -> LogicalTime;
}
#[derive(Debug)]
pub struct StandardTimeSource {
run_id: u64,
sequence: core::sync::atomic::AtomicU64,
}
impl StandardTimeSource {
#[must_use]
pub const fn new(run_id: u64) -> Self {
Self {
run_id,
sequence: core::sync::atomic::AtomicU64::new(0),
}
}
#[must_use]
pub const fn zero() -> Self {
Self::new(0)
}
}
impl TimeSource for StandardTimeSource {
fn now(&self) -> LogicalTime {
let seq = self.sequence.load(core::sync::atomic::Ordering::SeqCst);
LogicalTime::new(self.run_id, seq)
}
fn tick(&self) -> LogicalTime {
let seq = self.sequence.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
LogicalTime::new(self.run_id, seq)
}
}
#[derive(Debug)]
pub struct MockTimeSource {
run_id: u64,
time: core::sync::atomic::AtomicU64,
}
impl MockTimeSource {
#[must_use]
pub const fn new(run_id: u64, initial: u64) -> Self {
Self {
run_id,
time: core::sync::atomic::AtomicU64::new(initial),
}
}
pub fn set(&self, sequence: u64) {
self.time.store(sequence, core::sync::atomic::Ordering::SeqCst);
}
pub fn advance(&self, delta: u64) -> LogicalTime {
let seq = self.time.fetch_add(delta, core::sync::atomic::Ordering::SeqCst);
LogicalTime::new(self.run_id, seq)
}
}
impl TimeSource for MockTimeSource {
fn now(&self) -> LogicalTime {
let seq = self.time.load(core::sync::atomic::Ordering::SeqCst);
LogicalTime::new(self.run_id, seq)
}
fn tick(&self) -> LogicalTime {
let seq = self.time.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
LogicalTime::new(self.run_id, seq)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_logical_time_ordering() {
let t1 = LogicalTime::new(1, 10);
let t2 = LogicalTime::new(1, 11);
let t3 = LogicalTime::new(1, 10);
assert!(t2 > t1);
assert_eq!(t1, t3);
}
#[test]
fn test_logical_time_next() {
let t = LogicalTime::initial(5);
assert_eq!(t.sequence(), 0);
assert_eq!(t.next().sequence(), 1);
assert_eq!(t.next().next().sequence(), 2);
}
#[test]
fn test_standard_time_source() {
let source = StandardTimeSource::new(42);
assert_eq!(source.now().sequence(), 0);
assert_eq!(source.tick().sequence(), 0);
assert_eq!(source.now().sequence(), 1);
assert_eq!(source.tick().sequence(), 1);
}
}