prometheus/
timer.rs

1use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
2use std::thread;
3use std::time::{Duration, Instant};
4
5use lazy_static::lazy_static;
6
7/// Milliseconds since ANCHOR.
8static RECENT: AtomicU64 = AtomicU64::new(0);
9lazy_static! {
10    static ref ANCHOR: Instant = Instant::now();
11}
12
13/// Convert a duration to millisecond.
14#[inline]
15pub fn duration_to_millis(dur: Duration) -> u64 {
16    dur.as_secs() * 1000 + dur.subsec_millis() as u64
17}
18
19/// Returns milliseconds since ANCHOR.
20///
21/// ANCHOR is some fixed point in history.
22pub fn now_millis() -> u64 {
23    let res = Instant::now();
24    let t = duration_to_millis(res.saturating_duration_since(*ANCHOR));
25    let mut recent = RECENT.load(Ordering::Relaxed);
26    loop {
27        if recent > t {
28            return recent;
29        }
30        match RECENT.compare_exchange_weak(recent, t, Ordering::Relaxed, Ordering::Relaxed) {
31            Ok(_) => return t,
32            Err(r) => recent = r,
33        }
34    }
35}
36
37/// Returns recent returned value by `now_millis`.
38pub fn recent_millis() -> u64 {
39    RECENT.load(Ordering::Relaxed)
40}
41
42lazy_static! {
43    static ref UPDATER_IS_RUNNING: AtomicBool = AtomicBool::new(false);
44}
45
46const CHECK_UPDATE_INTERVAL: Duration = Duration::from_millis(200);
47
48/// Ensures background updater is running, which will call `now_millis` periodically.
49pub fn ensure_updater() {
50    if UPDATER_IS_RUNNING
51        .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
52        .is_ok()
53    {
54        std::thread::Builder::new()
55            .name("time updater".to_owned())
56            .spawn(|| loop {
57                thread::sleep(CHECK_UPDATE_INTERVAL);
58                now_millis();
59            })
60            .unwrap();
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use std::thread;
67    use std::time::Duration;
68
69    #[test]
70    fn test_duration_to_millis() {
71        let cases = vec![(1, 1, 1000), (0, 1_000_000, 1), (3, 103_000_000, 3103)];
72        for (secs, nanos, exp) in cases {
73            let dur = Duration::new(secs, nanos);
74            assert_eq!(super::duration_to_millis(dur), exp);
75        }
76    }
77
78    #[test]
79    fn test_time_update() {
80        assert_eq!(super::recent_millis(), 0);
81        let now = super::now_millis();
82        assert_eq!(super::recent_millis(), now);
83        super::ensure_updater();
84        thread::sleep(super::CHECK_UPDATE_INTERVAL * 2);
85        assert!(super::recent_millis() > now);
86    }
87}