use std::sync::atomic::{AtomicI64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Debug)]
pub struct TimeSyncTracker {
last_sync: AtomicI64,
}
impl Default for TimeSyncTracker {
fn default() -> Self {
Self::new()
}
}
impl TimeSyncTracker {
pub fn new() -> Self {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs() as i64;
Self {
last_sync: AtomicI64::new(now),
}
}
pub fn with_last_sync(timestamp: i64) -> Self {
Self {
last_sync: AtomicI64::new(timestamp),
}
}
pub fn mark_synced(&self) {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs() as i64;
self.last_sync.store(now, Ordering::SeqCst);
}
pub fn set_last_sync(&self, timestamp: i64) {
self.last_sync.store(timestamp, Ordering::SeqCst);
}
pub fn last_sync_time(&self) -> i64 {
self.last_sync.load(Ordering::SeqCst)
}
pub fn lts(&self) -> i64 {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs() as i64;
let last = self.last_sync.load(Ordering::SeqCst);
(now - last).max(0)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread::sleep;
use std::time::Duration;
#[test]
fn test_new_tracker() {
let tracker = TimeSyncTracker::new();
assert!(tracker.lts() < 2);
}
#[test]
fn test_lts_increases() {
let tracker = TimeSyncTracker::new();
let lts1 = tracker.lts();
sleep(Duration::from_millis(100));
let lts2 = tracker.lts();
assert!(lts2 >= lts1);
}
#[test]
fn test_mark_synced_resets() {
let old_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as i64
- 100;
let tracker = TimeSyncTracker::with_last_sync(old_time);
assert!(tracker.lts() >= 100);
tracker.mark_synced();
assert!(tracker.lts() < 2);
}
}