use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::thread;
use std::time::{Duration, Instant};
use lazy_static::lazy_static;
static RECENT: AtomicU64 = AtomicU64::new(0);
lazy_static! {
static ref ANCHOR: Instant = Instant::now();
}
#[inline]
pub fn duration_to_millis(dur: Duration) -> u64 {
dur.as_secs() * 1000 + dur.subsec_millis() as u64
}
pub fn now_millis() -> u64 {
let res = Instant::now();
let t = duration_to_millis(res.saturating_duration_since(*ANCHOR));
let mut recent = RECENT.load(Ordering::Relaxed);
loop {
if recent > t {
return recent;
}
match RECENT.compare_exchange_weak(recent, t, Ordering::Relaxed, Ordering::Relaxed) {
Ok(_) => return t,
Err(r) => recent = r,
}
}
}
pub fn recent_millis() -> u64 {
RECENT.load(Ordering::Relaxed)
}
lazy_static! {
static ref UPDATER_IS_RUNNING: AtomicBool = AtomicBool::new(false);
}
const CHECK_UPDATE_INTERVAL: Duration = Duration::from_millis(200);
pub fn ensure_updater() {
if UPDATER_IS_RUNNING
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
.is_ok()
{
std::thread::Builder::new()
.name("time updater".to_owned())
.spawn(|| loop {
thread::sleep(CHECK_UPDATE_INTERVAL);
now_millis();
})
.unwrap();
}
}
#[cfg(test)]
mod tests {
use std::thread;
use std::time::Duration;
#[test]
fn test_duration_to_millis() {
let cases = vec![(1, 1, 1000), (0, 1_000_000, 1), (3, 103_000_000, 3103)];
for (secs, nanos, exp) in cases {
let dur = Duration::new(secs, nanos);
assert_eq!(super::duration_to_millis(dur), exp);
}
}
#[test]
fn test_time_update() {
assert_eq!(super::recent_millis(), 0);
let now = super::now_millis();
assert_eq!(super::recent_millis(), now);
super::ensure_updater();
thread::sleep(super::CHECK_UPDATE_INTERVAL * 2);
assert!(super::recent_millis() > now);
}
}