redux/
instant.rs

1use std::{
2    cell::RefCell,
3    cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd},
4    ops::{Add, AddAssign, Sub, SubAssign},
5    time::Duration,
6};
7
8use crate::SystemTime;
9
10#[cfg(not(target_arch = "wasm32"))]
11use std::time::Instant as InnerInstant;
12
13#[cfg(target_arch = "wasm32")]
14use wasm_timer::Instant as InnerInstant;
15
16#[derive(Copy, Clone)]
17pub struct Instant {
18    inner: InnerInstant,
19}
20
21impl PartialEq for Instant {
22    fn eq(&self, other: &Instant) -> bool {
23        self.inner == other.inner
24    }
25}
26
27impl Eq for Instant {}
28
29impl PartialOrd for Instant {
30    fn partial_cmp(&self, other: &Instant) -> Option<Ordering> {
31        Some(self.cmp(other))
32    }
33}
34
35impl Ord for Instant {
36    fn cmp(&self, other: &Self) -> Ordering {
37        self.inner.partial_cmp(&other.inner).unwrap()
38    }
39}
40
41thread_local! {
42    static INITIAL_AND_DRIFT: RefCell<Option<(SystemTime, InnerInstant, Duration)>> = const { RefCell::new(None) };
43}
44
45impl Instant {
46    pub fn now() -> Instant {
47        let inner = INITIAL_AND_DRIFT.with_borrow_mut(|initial_and_drift| {
48            let (initial_sys_time, initial_monotonic, drift) = initial_and_drift
49                .get_or_insert_with(|| (SystemTime::now(), InnerInstant::now(), Duration::ZERO));
50
51            let sys_time_passed = SystemTime::now()
52                .duration_since(*initial_sys_time)
53                .unwrap_or_default();
54            let monotonic_now = InnerInstant::now();
55            let monotonic_passed = monotonic_now.duration_since(*initial_monotonic);
56
57            if sys_time_passed > monotonic_passed + *drift {
58                // handling for system suspension/browser tab suspension.
59                *drift = sys_time_passed - monotonic_passed;
60            }
61
62            monotonic_now + *drift
63        });
64
65        Self { inner }
66    }
67
68    pub fn duration_since(&self, earlier: Instant) -> Duration {
69        *self - earlier
70    }
71
72    pub fn elapsed(&self) -> Duration {
73        Instant::now() - *self
74    }
75
76    pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
77        match self.cmp(&earlier) {
78            Ordering::Less => None,
79            _ => Some(self.duration_since(earlier)),
80        }
81    }
82
83    pub fn saturating_duration_since(&self, earlier: Instant) -> Duration {
84        self.checked_duration_since(earlier).unwrap_or_default()
85    }
86
87    pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
88        Some(*self + duration)
89    }
90
91    pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
92        Some(*self - duration)
93    }
94}
95
96impl From<InnerInstant> for Instant {
97    fn from(inner: InnerInstant) -> Self {
98        Self { inner }
99    }
100}
101
102impl Add<Duration> for Instant {
103    type Output = Instant;
104
105    fn add(self, other: Duration) -> Instant {
106        Instant {
107            inner: self.inner + other,
108        }
109    }
110}
111
112impl Sub<Duration> for Instant {
113    type Output = Instant;
114
115    fn sub(self, other: Duration) -> Instant {
116        Instant {
117            inner: self.inner - other,
118        }
119    }
120}
121
122impl Sub<Instant> for Instant {
123    type Output = Duration;
124
125    fn sub(self, other: Instant) -> Duration {
126        self.inner - other.inner
127    }
128}
129
130impl AddAssign<Duration> for Instant {
131    fn add_assign(&mut self, other: Duration) {
132        *self = *self + other;
133    }
134}
135
136impl SubAssign<Duration> for Instant {
137    fn sub_assign(&mut self, other: Duration) {
138        *self = *self - other;
139    }
140}
141
142impl std::fmt::Debug for Instant {
143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144        self.inner.fmt(f)
145    }
146}