use std::sync::atomic::{AtomicU64, Ordering};
pub struct TimestampGenerator {
clock: quanta::Clock,
baseline_raw: u64,
last: AtomicU64,
}
impl TimestampGenerator {
pub fn new() -> Self {
let clock = quanta::Clock::new();
let baseline_raw = clock.raw();
Self {
clock,
baseline_raw,
last: AtomicU64::new(0),
}
}
#[inline(always)]
pub fn next(&self) -> u64 {
loop {
let raw = self.clock.raw();
let now = self.clock.delta_as_nanos(self.baseline_raw, raw);
let last = self.last.load(Ordering::Acquire);
let next = match last.checked_add(1) {
Some(inc) => inc,
None => panic!("TimestampGenerator: timestamp space exhausted (u64::MAX)"),
};
let ts = now.max(next);
if ts == u64::MAX {
panic!(
"TimestampGenerator: timestamp space exhausted (would return u64::MAX); \
last={last}, now={now}",
);
}
match self
.last
.compare_exchange_weak(last, ts, Ordering::Release, Ordering::Relaxed)
{
Ok(_) => return ts,
Err(_) => {
std::hint::spin_loop();
}
}
}
}
#[inline(always)]
pub fn now_raw(&self) -> u64 {
self.clock.raw()
}
#[inline]
pub fn raw_to_nanos(&self, raw: u64) -> u64 {
self.clock.delta_as_nanos(self.baseline_raw, raw)
}
#[inline]
pub fn last(&self) -> u64 {
self.last.load(Ordering::Acquire)
}
}
impl Default for TimestampGenerator {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[test]
fn test_monotonicity() {
let ts_gen = TimestampGenerator::new();
let mut prev = 0u64;
for _ in 0..10_000 {
let ts = ts_gen.next();
assert!(ts > prev, "timestamps must be strictly increasing");
prev = ts;
}
}
#[test]
fn timestamp_next_reads_tsc_inside_cas_loop() {
let src = include_str!("timestamp.rs");
let header = "pub fn next(&self) -> u64 {";
let start = src
.find(header)
.expect("TimestampGenerator::next must exist");
let after_header = start + header.len();
let next_fn = src[after_header..]
.find("\n pub fn ")
.or_else(|| src[after_header..].find("\n fn "))
.expect("a sibling fn must follow next()")
+ after_header;
let body = &src[start..next_fn];
let body_no_comments: String = body
.lines()
.map(|l| match l.find("//") {
Some(idx) => &l[..idx],
None => l,
})
.collect::<Vec<_>>()
.join("\n");
let loop_idx = body_no_comments
.find("loop {")
.expect("CAS loop must exist in next()");
let loop_body = &body_no_comments[loop_idx..];
assert!(
loop_body.contains("self.clock.raw()"),
"regression: `self.clock.raw()` must be inside the CAS \
loop in TimestampGenerator::next. Hoisted outside, a \
retry under contention reuses the stale TSC and the \
returned timestamp drifts behind real time."
);
assert!(
loop_body.contains("delta_as_nanos"),
"regression: `delta_as_nanos` must be inside the CAS \
loop alongside the TSC read."
);
}
#[test]
fn test_monotonicity_concurrent() {
let ts_gen = std::sync::Arc::new(TimestampGenerator::new());
let mut handles = vec![];
for _ in 0..4 {
let ts_gen_clone = ts_gen.clone();
handles.push(thread::spawn(move || {
let mut timestamps = Vec::with_capacity(1000);
for _ in 0..1000 {
timestamps.push(ts_gen_clone.next());
}
timestamps
}));
}
let mut all_timestamps: Vec<u64> = handles
.into_iter()
.flat_map(|h| h.join().unwrap())
.collect();
all_timestamps.sort();
let unique_count = all_timestamps.windows(2).filter(|w| w[0] != w[1]).count() + 1;
assert_eq!(
unique_count,
all_timestamps.len(),
"all timestamps must be unique"
);
}
#[test]
fn test_no_syscall_performance() {
let ts_gen = TimestampGenerator::new();
for _ in 0..1000 {
let _ = ts_gen.next();
}
let start = std::time::Instant::now();
let iterations = 100_000;
for _ in 0..iterations {
let _ = ts_gen.next();
}
let elapsed = start.elapsed();
let per_call = elapsed.as_nanos() / iterations as u128;
assert!(
per_call < 500,
"timestamp generation too slow: {}ns per call",
per_call
);
}
#[test]
fn test_timestamp_generator_new() {
let ts_gen = TimestampGenerator::new();
assert_eq!(ts_gen.last(), 0);
}
#[test]
fn test_timestamp_generator_default() {
let ts_gen = TimestampGenerator::default();
assert_eq!(ts_gen.last(), 0);
}
#[test]
fn test_now_raw() {
let ts_gen = TimestampGenerator::new();
let raw1 = ts_gen.now_raw();
let raw2 = ts_gen.now_raw();
assert!(raw2 >= raw1 || raw1 - raw2 < 1000); }
#[test]
fn test_raw_to_nanos() {
let ts_gen = TimestampGenerator::new();
let raw = ts_gen.now_raw();
let nanos = ts_gen.raw_to_nanos(raw);
assert!(nanos > 0);
}
#[test]
fn test_raw_to_nanos_zero() {
let ts_gen = TimestampGenerator::new();
let nanos = ts_gen.raw_to_nanos(0);
assert_eq!(nanos, 0);
}
#[test]
fn test_last_after_next() {
let ts_gen = TimestampGenerator::new();
let ts1 = ts_gen.next();
assert_eq!(ts_gen.last(), ts1);
let ts2 = ts_gen.next();
assert_eq!(ts_gen.last(), ts2);
assert!(ts2 > ts1);
}
#[test]
fn test_next_returns_increasing_values() {
let ts_gen = TimestampGenerator::new();
let mut prev = ts_gen.next();
for _ in 0..100 {
let current = ts_gen.next();
assert!(current > prev);
prev = current;
}
}
#[test]
fn test_multiple_generators_independent() {
let ts_gen1 = TimestampGenerator::new();
let ts_gen2 = TimestampGenerator::new();
let ts1 = ts_gen1.next();
let ts2 = ts_gen2.next();
assert!(ts1 > 0);
assert!(ts2 > 0);
assert_eq!(ts_gen1.last(), ts1);
assert_eq!(ts_gen2.last(), ts2);
}
#[test]
fn test_now_raw_does_not_affect_last() {
let ts_gen = TimestampGenerator::new();
let initial_last = ts_gen.last();
let _ = ts_gen.now_raw();
let _ = ts_gen.now_raw();
let _ = ts_gen.now_raw();
assert_eq!(ts_gen.last(), initial_last);
}
#[test]
fn test_rapid_calls() {
let ts_gen = TimestampGenerator::new();
let mut timestamps = Vec::with_capacity(10000);
for _ in 0..10000 {
timestamps.push(ts_gen.next());
}
for window in timestamps.windows(2) {
assert!(window[1] > window[0]);
}
}
#[test]
#[should_panic(expected = "timestamp space exhausted")]
fn test_next_panics_at_u64_max() {
let ts_gen = TimestampGenerator::new();
ts_gen.last.store(u64::MAX, Ordering::Release);
let _ = ts_gen.next();
}
#[test]
fn test_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<TimestampGenerator>();
}
#[test]
fn next_returns_nanoseconds_not_raw_ticks() {
let ts_gen = TimestampGenerator::new();
let t0 = ts_gen.next();
std::thread::sleep(std::time::Duration::from_millis(50));
let t1 = ts_gen.next();
let delta = t1 - t0;
let ns_lo = 25_000_000u64;
let ns_hi = 200_000_000u64;
assert!(
delta >= ns_lo && delta <= ns_hi,
"delta over a 50ms sleep was {delta} — outside the {ns_lo}..={ns_hi} \
ns window. Most likely the timestamp is in raw TSC ticks again \
(would be ~150_000_000 on a 3 GHz core)."
);
}
#[test]
fn next_first_call_is_close_to_zero() {
let ts_gen = TimestampGenerator::new();
let first = ts_gen.next();
let ten_ms_ns = 10_000_000u64;
assert!(
first < ten_ms_ns,
"first next() returned {first} ns; expected < {ten_ms_ns} ns. \
Pre-fix this would be ~uptime in ns."
);
}
}