sync_utils/
time.rs

1use std::sync::atomic::{AtomicU64, Ordering};
2
3use chrono::offset::Utc;
4
5static DELAYED_SEC: AtomicU64 = AtomicU64::new(0);
6
7#[inline]
8pub fn timestamp() -> u64 {
9    Utc::now().timestamp() as u64
10}
11
12#[inline]
13pub fn timestamp_subsec_nanos() -> (u64, u32) {
14    let dt = Utc::now();
15    (dt.timestamp() as u64, dt.timestamp_subsec_nanos())
16}
17
18/// Light-weight timestamp that update unix epoch every second in background,
19/// to minimise the cost of time related system calls.
20pub struct DelayedTime();
21
22impl DelayedTime {
23    /// Start the background time update thread once, and return the timestamp.
24    ///
25    /// NOTE: After program started or fork, remember to call this function.
26    #[inline(always)]
27    pub fn start() -> u64 {
28        let now = timestamp();
29        match DELAYED_SEC.compare_exchange(0, now, Ordering::SeqCst, Ordering::Acquire) {
30            Ok(_) => {
31                std::thread::spawn(|| {
32                    let d = std::time::Duration::from_secs(1);
33                    loop {
34                        DELAYED_SEC.store(timestamp(), Ordering::Release);
35                        std::thread::sleep(d);
36                    }
37                });
38                now
39            }
40            Err(_now) => _now,
41        }
42    }
43
44    /// Get the delayed timestamp in second. Start the update thread if not started.
45    #[inline(always)]
46    pub fn now() -> u64 {
47        let now = DELAYED_SEC.load(Ordering::Acquire);
48        if now == 0 { Self::start() } else { now }
49    }
50
51    /// Get the delayed timestamp in second. Use acture time when update thread not running.
52    #[inline(always)]
53    pub fn get() -> u64 {
54        let now = DELAYED_SEC.load(Ordering::Acquire);
55        if now == 0 { timestamp() } else { now }
56    }
57}
58
59/// When the time (unix epoch) has been too long since `pre_ts`, return true.
60///
61/// `pre_ts`: old timestamp.
62///
63/// `duration`: unit in seconds.
64#[inline]
65pub fn check_timelapse(pre_ts: u64, duration: u64) -> bool {
66    let now = DelayedTime::get();
67    if now <= pre_ts {
68        return false;
69    }
70    now - pre_ts >= duration
71}