#![allow(dead_code)]
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
use super::NodeId;
pub struct LogicalClock {
node_id: NodeId,
clock: AtomicU64,
}
impl LogicalClock {
pub fn new(node_id: NodeId) -> Self {
let now = Self::physical_time();
Self {
node_id,
clock: AtomicU64::new(now),
}
}
fn physical_time() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0)
}
pub fn current(&self) -> u64 {
self.clock.load(Ordering::SeqCst)
}
pub fn tick(&self) -> u64 {
loop {
let current = self.clock.load(Ordering::SeqCst);
let physical = Self::physical_time();
let new_value = (current + 1).max(physical);
match self.clock.compare_exchange(
current,
new_value,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(_) => return new_value,
Err(_) => continue, }
}
}
pub fn update(&self, received: u64) {
loop {
let current = self.clock.load(Ordering::SeqCst);
let physical = Self::physical_time();
let new_value = current.max(received).max(physical);
if new_value <= current {
return; }
match self.clock.compare_exchange(
current,
new_value,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(_) => return,
Err(_) => continue,
}
}
}
pub fn node_id(&self) -> NodeId {
self.node_id
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_clock_monotonicity() {
let clock = LogicalClock::new(1);
let t1 = clock.tick();
let t2 = clock.tick();
let t3 = clock.tick();
assert!(t2 > t1);
assert!(t3 > t2);
}
#[test]
fn test_clock_update() {
let clock = LogicalClock::new(1);
let t1 = clock.tick();
let future = t1 + 1000;
clock.update(future);
let t2 = clock.current();
assert!(t2 >= future);
let t3 = clock.tick();
assert!(t3 > t2);
}
#[test]
fn test_clock_update_with_past() {
let clock = LogicalClock::new(1);
let t1 = clock.tick();
clock.update(t1 - 1000);
let t2 = clock.current();
assert!(t2 >= t1);
}
}