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
7static RECENT: AtomicU64 = AtomicU64::new(0);
9lazy_static! {
10 static ref ANCHOR: Instant = Instant::now();
11}
12
13#[inline]
15pub fn duration_to_millis(dur: Duration) -> u64 {
16 dur.as_secs() * 1000 + dur.subsec_millis() as u64
17}
18
19pub 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
37pub 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
48pub 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}