use std::sync::atomic::{AtomicI64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
pub(crate) fn now_ms() -> i64 {
static LAST: AtomicI64 = AtomicI64::new(0);
let wall = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|elapsed| elapsed.as_millis() as i64)
.unwrap_or(0);
let mut previous = LAST.load(Ordering::Relaxed);
loop {
let next = if wall > previous { wall } else { previous + 1 };
match LAST.compare_exchange_weak(previous, next, Ordering::Relaxed, Ordering::Relaxed) {
Ok(_) => return next,
Err(actual) => previous = actual,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn returns_real_wall_clock_not_a_counter() {
let now = now_ms();
assert!(
now > 1_600_000_000_000,
"expected epoch milliseconds, got {now}"
);
}
#[test]
fn strictly_increasing_within_a_millisecond() {
let first = now_ms();
let second = now_ms();
let third = now_ms();
assert!(second > first, "{second} !> {first}");
assert!(third > second, "{third} !> {second}");
}
#[test]
fn concurrent_callers_never_see_a_duplicate_or_a_regression() {
let handles: Vec<_> = (0..8)
.map(|_| std::thread::spawn(|| (0..500).map(|_| now_ms()).collect::<Vec<_>>()))
.collect();
let mut all: Vec<i64> = Vec::new();
for handle in handles {
all.extend(handle.join().expect("thread"));
}
all.sort_unstable();
let before = all.len();
all.dedup();
assert_eq!(all.len(), before, "duplicate timestamps was observed");
}
}