]>
Commit | Line | Data |
---|---|---|
532ac7d7 | 1 | use crate::cmp::Ordering; |
dfeec247 | 2 | use crate::convert::TryInto; |
532ac7d7 XL |
3 | use crate::fmt; |
4 | use crate::mem; | |
5 | use crate::sys::c; | |
6 | use crate::time::Duration; | |
532ac7d7 | 7 | |
ff7c6d11 | 8 | use core::hash::{Hash, Hasher}; |
85aaf69f | 9 | |
d9579d0f | 10 | const NANOS_PER_SEC: u64 = 1_000_000_000; |
92a42be0 | 11 | const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100; |
c34b1796 | 12 | |
ff7c6d11 | 13 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] |
92a42be0 | 14 | pub struct Instant { |
9fa01778 XL |
15 | // This duration is relative to an arbitrary microsecond epoch |
16 | // from the winapi QueryPerformanceCounter function. | |
17 | t: Duration, | |
85aaf69f SL |
18 | } |
19 | ||
92a42be0 SL |
20 | #[derive(Copy, Clone)] |
21 | pub struct SystemTime { | |
22 | t: c::FILETIME, | |
23 | } | |
24 | ||
25 | const INTERVALS_TO_UNIX_EPOCH: u64 = 11_644_473_600 * INTERVALS_PER_SEC; | |
26 | ||
27 | pub const UNIX_EPOCH: SystemTime = SystemTime { | |
28 | t: c::FILETIME { | |
29 | dwLowDateTime: INTERVALS_TO_UNIX_EPOCH as u32, | |
30 | dwHighDateTime: (INTERVALS_TO_UNIX_EPOCH >> 32) as u32, | |
31 | }, | |
32 | }; | |
33 | ||
34 | impl Instant { | |
35 | pub fn now() -> Instant { | |
9fa01778 XL |
36 | // High precision timing on windows operates in "Performance Counter" |
37 | // units, as returned by the WINAPI QueryPerformanceCounter function. | |
38 | // These relate to seconds by a factor of QueryPerformanceFrequency. | |
39 | // In order to keep unit conversions out of normal interval math, we | |
40 | // measure in QPC units and immediately convert to nanoseconds. | |
41 | perf_counter::PerformanceCounterInstant::now().into() | |
85aaf69f | 42 | } |
92a42be0 | 43 | |
0731742a XL |
44 | pub fn actually_monotonic() -> bool { |
45 | false | |
46 | } | |
47 | ||
48 | pub const fn zero() -> Instant { | |
9fa01778 | 49 | Instant { t: Duration::from_secs(0) } |
0731742a XL |
50 | } |
51 | ||
532ac7d7 | 52 | pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { |
9fa01778 XL |
53 | // On windows there's a threshold below which we consider two timestamps |
54 | // equivalent due to measurement error. For more details + doc link, | |
55 | // check the docs on epsilon. | |
dfeec247 | 56 | let epsilon = perf_counter::PerformanceCounterInstant::epsilon(); |
9fa01778 | 57 | if other.t > self.t && other.t - self.t <= epsilon { |
532ac7d7 XL |
58 | Some(Duration::new(0, 0)) |
59 | } else { | |
60 | self.t.checked_sub(other.t) | |
92a42be0 | 61 | } |
92a42be0 SL |
62 | } |
63 | ||
0731742a | 64 | pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { |
dfeec247 | 65 | Some(Instant { t: self.t.checked_add(*other)? }) |
92a42be0 SL |
66 | } |
67 | ||
0731742a | 68 | pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { |
dfeec247 | 69 | Some(Instant { t: self.t.checked_sub(*other)? }) |
92a42be0 | 70 | } |
85aaf69f SL |
71 | } |
72 | ||
92a42be0 SL |
73 | impl SystemTime { |
74 | pub fn now() -> SystemTime { | |
75 | unsafe { | |
76 | let mut t: SystemTime = mem::zeroed(); | |
ba9703b0 | 77 | c::GetSystemTimePreciseAsFileTime(&mut t.t); |
e74abb32 | 78 | t |
92a42be0 SL |
79 | } |
80 | } | |
85aaf69f | 81 | |
92a42be0 SL |
82 | fn from_intervals(intervals: i64) -> SystemTime { |
83 | SystemTime { | |
84 | t: c::FILETIME { | |
85 | dwLowDateTime: intervals as c::DWORD, | |
86 | dwHighDateTime: (intervals >> 32) as c::DWORD, | |
dfeec247 | 87 | }, |
92a42be0 SL |
88 | } |
89 | } | |
90 | ||
91 | fn intervals(&self) -> i64 { | |
92 | (self.t.dwLowDateTime as i64) | ((self.t.dwHighDateTime as i64) << 32) | |
93 | } | |
94 | ||
95 | pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> { | |
96 | let me = self.intervals(); | |
97 | let other = other.intervals(); | |
98 | if me >= other { | |
99 | Ok(intervals2dur((me - other) as u64)) | |
100 | } else { | |
101 | Err(intervals2dur((other - me) as u64)) | |
102 | } | |
103 | } | |
104 | ||
a1dfa0c6 | 105 | pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { |
0731742a XL |
106 | let intervals = self.intervals().checked_add(checked_dur2intervals(other)?)?; |
107 | Some(SystemTime::from_intervals(intervals)) | |
92a42be0 SL |
108 | } |
109 | ||
0731742a XL |
110 | pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { |
111 | let intervals = self.intervals().checked_sub(checked_dur2intervals(other)?)?; | |
112 | Some(SystemTime::from_intervals(intervals)) | |
92a42be0 SL |
113 | } |
114 | } | |
115 | ||
116 | impl PartialEq for SystemTime { | |
117 | fn eq(&self, other: &SystemTime) -> bool { | |
118 | self.intervals() == other.intervals() | |
85aaf69f SL |
119 | } |
120 | } | |
121 | ||
92a42be0 | 122 | impl Eq for SystemTime {} |
85aaf69f | 123 | |
92a42be0 SL |
124 | impl PartialOrd for SystemTime { |
125 | fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> { | |
126 | Some(self.cmp(other)) | |
127 | } | |
128 | } | |
129 | ||
130 | impl Ord for SystemTime { | |
131 | fn cmp(&self, other: &SystemTime) -> Ordering { | |
132 | self.intervals().cmp(&other.intervals()) | |
85aaf69f SL |
133 | } |
134 | } | |
c34b1796 | 135 | |
92a42be0 | 136 | impl fmt::Debug for SystemTime { |
532ac7d7 | 137 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
dfeec247 | 138 | f.debug_struct("SystemTime").field("intervals", &self.intervals()).finish() |
92a42be0 SL |
139 | } |
140 | } | |
141 | ||
7453a54e SL |
142 | impl From<c::FILETIME> for SystemTime { |
143 | fn from(t: c::FILETIME) -> SystemTime { | |
a1dfa0c6 | 144 | SystemTime { t } |
7453a54e SL |
145 | } |
146 | } | |
147 | ||
ff7c6d11 | 148 | impl Hash for SystemTime { |
dfeec247 | 149 | fn hash<H: Hasher>(&self, state: &mut H) { |
ff7c6d11 XL |
150 | self.intervals().hash(state) |
151 | } | |
152 | } | |
153 | ||
0731742a XL |
154 | fn checked_dur2intervals(dur: &Duration) -> Option<i64> { |
155 | dur.as_secs() | |
156 | .checked_mul(INTERVALS_PER_SEC)? | |
157 | .checked_add(dur.subsec_nanos() as u64 / 100)? | |
158 | .try_into() | |
159 | .ok() | |
c34b1796 AL |
160 | } |
161 | ||
92a42be0 | 162 | fn intervals2dur(intervals: u64) -> Duration { |
dfeec247 | 163 | Duration::new(intervals / INTERVALS_PER_SEC, ((intervals % INTERVALS_PER_SEC) * 100) as u32) |
92a42be0 SL |
164 | } |
165 | ||
9fa01778 | 166 | mod perf_counter { |
dfeec247 | 167 | use super::NANOS_PER_SEC; |
532ac7d7 | 168 | use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; |
532ac7d7 XL |
169 | use crate::sys::c; |
170 | use crate::sys::cvt; | |
dfeec247 | 171 | use crate::sys_common::mul_div_u64; |
532ac7d7 | 172 | use crate::time::Duration; |
9fa01778 XL |
173 | |
174 | pub struct PerformanceCounterInstant { | |
dfeec247 | 175 | ts: c::LARGE_INTEGER, |
9fa01778 XL |
176 | } |
177 | impl PerformanceCounterInstant { | |
178 | pub fn now() -> Self { | |
dfeec247 | 179 | Self { ts: query() } |
9fa01778 | 180 | } |
92a42be0 | 181 | |
9fa01778 XL |
182 | // Per microsoft docs, the margin of error for cross-thread time comparisons |
183 | // using QueryPerformanceCounter is 1 "tick" -- defined as 1/frequency(). | |
184 | // Reference: https://docs.microsoft.com/en-us/windows/desktop/SysInfo | |
185 | // /acquiring-high-resolution-time-stamps | |
186 | pub fn epsilon() -> Duration { | |
187 | let epsilon = NANOS_PER_SEC / (frequency() as u64); | |
188 | Duration::from_nanos(epsilon) | |
189 | } | |
190 | } | |
191 | impl From<PerformanceCounterInstant> for super::Instant { | |
192 | fn from(other: PerformanceCounterInstant) -> Self { | |
193 | let freq = frequency() as u64; | |
194 | let instant_nsec = mul_div_u64(other.ts as u64, NANOS_PER_SEC, freq); | |
dfeec247 | 195 | Self { t: Duration::from_nanos(instant_nsec) } |
9fa01778 XL |
196 | } |
197 | } | |
198 | ||
199 | fn frequency() -> c::LARGE_INTEGER { | |
200 | static mut FREQUENCY: c::LARGE_INTEGER = 0; | |
532ac7d7 | 201 | static STATE: AtomicUsize = AtomicUsize::new(0); |
9fa01778 XL |
202 | |
203 | unsafe { | |
532ac7d7 XL |
204 | // If a previous thread has filled in this global state, use that. |
205 | if STATE.load(SeqCst) == 2 { | |
206 | return FREQUENCY; | |
207 | } | |
208 | ||
209 | // ... otherwise learn for ourselves ... | |
210 | let mut frequency = 0; | |
211 | cvt(c::QueryPerformanceFrequency(&mut frequency)).unwrap(); | |
212 | ||
213 | // ... and attempt to be the one thread that stores it globally for | |
214 | // all other threads | |
215 | if STATE.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() { | |
216 | FREQUENCY = frequency; | |
217 | STATE.store(2, SeqCst); | |
218 | } | |
e74abb32 | 219 | frequency |
9fa01778 XL |
220 | } |
221 | } | |
222 | ||
223 | fn query() -> c::LARGE_INTEGER { | |
224 | let mut qpc_value: c::LARGE_INTEGER = 0; | |
dfeec247 | 225 | cvt(unsafe { c::QueryPerformanceCounter(&mut qpc_value) }).unwrap(); |
9fa01778 | 226 | qpc_value |
92a42be0 | 227 | } |
c34b1796 | 228 | } |