]>
Commit | Line | Data |
---|---|---|
04454e1e | 1 | use crate::fmt; |
532ac7d7 XL |
2 | use crate::time::Duration; |
3 | ||
04454e1e | 4 | pub use self::inner::Instant; |
85aaf69f | 5 | |
d9579d0f | 6 | const NSEC_PER_SEC: u64 = 1_000_000_000; |
04454e1e FG |
7 | pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; |
8 | ||
9 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | |
10 | pub struct SystemTime { | |
11 | pub(in crate::sys::unix) t: Timespec, | |
12 | } | |
13 | ||
14 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | |
15 | pub(in crate::sys::unix) struct Timespec { | |
16 | tv_sec: i64, | |
17 | tv_nsec: i64, | |
18 | } | |
19 | ||
20 | impl SystemTime { | |
923072b8 | 21 | #[cfg_attr(target_os = "horizon", allow(unused))] |
04454e1e FG |
22 | pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { |
23 | SystemTime { t: Timespec::new(tv_sec, tv_nsec) } | |
24 | } | |
25 | ||
26 | pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> { | |
27 | self.t.sub_timespec(&other.t) | |
28 | } | |
d9579d0f | 29 | |
04454e1e FG |
30 | pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { |
31 | Some(SystemTime { t: self.t.checked_add_duration(other)? }) | |
32 | } | |
33 | ||
34 | pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { | |
35 | Some(SystemTime { t: self.t.checked_sub_duration(other)? }) | |
36 | } | |
37 | } | |
38 | ||
39 | impl From<libc::timespec> for SystemTime { | |
40 | fn from(t: libc::timespec) -> SystemTime { | |
41 | SystemTime { t: Timespec::from(t) } | |
42 | } | |
43 | } | |
44 | ||
45 | impl fmt::Debug for SystemTime { | |
46 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
47 | f.debug_struct("SystemTime") | |
48 | .field("tv_sec", &self.t.tv_sec) | |
49 | .field("tv_nsec", &self.t.tv_nsec) | |
50 | .finish() | |
51 | } | |
a7813a04 XL |
52 | } |
53 | ||
54 | impl Timespec { | |
04454e1e FG |
55 | pub const fn zero() -> Timespec { |
56 | Timespec { tv_sec: 0, tv_nsec: 0 } | |
0731742a XL |
57 | } |
58 | ||
04454e1e FG |
59 | fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { |
60 | Timespec { tv_sec, tv_nsec } | |
61 | } | |
62 | ||
63 | pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { | |
a7813a04 | 64 | if self >= other { |
3dfed10e XL |
65 | // NOTE(eddyb) two aspects of this `if`-`else` are required for LLVM |
66 | // to optimize it into a branchless form (see also #75545): | |
67 | // | |
04454e1e | 68 | // 1. `self.tv_sec - other.tv_sec` shows up as a common expression |
3dfed10e XL |
69 | // in both branches, i.e. the `else` must have its `- 1` |
70 | // subtraction after the common one, not interleaved with it | |
04454e1e | 71 | // (it used to be `self.tv_sec - 1 - other.tv_sec`) |
3dfed10e XL |
72 | // |
73 | // 2. the `Duration::new` call (or any other additional complexity) | |
74 | // is outside of the `if`-`else`, not duplicated in both branches | |
75 | // | |
76 | // Ideally this code could be rearranged such that it more | |
77 | // directly expresses the lower-cost behavior we want from it. | |
04454e1e FG |
78 | let (secs, nsec) = if self.tv_nsec >= other.tv_nsec { |
79 | ((self.tv_sec - other.tv_sec) as u64, (self.tv_nsec - other.tv_nsec) as u32) | |
a7813a04 | 80 | } else { |
3dfed10e | 81 | ( |
04454e1e FG |
82 | (self.tv_sec - other.tv_sec - 1) as u64, |
83 | self.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.tv_nsec as u32, | |
dfeec247 | 84 | ) |
3dfed10e XL |
85 | }; |
86 | ||
87 | Ok(Duration::new(secs, nsec)) | |
a7813a04 XL |
88 | } else { |
89 | match other.sub_timespec(self) { | |
90 | Ok(d) => Err(d), | |
91 | Err(d) => Ok(d), | |
92 | } | |
93 | } | |
94 | } | |
95 | ||
04454e1e | 96 | pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> { |
ea8adc8c XL |
97 | let mut secs = other |
98 | .as_secs() | |
04454e1e | 99 | .try_into() // <- target type would be `i64` |
ea8adc8c | 100 | .ok() |
04454e1e | 101 | .and_then(|secs| self.tv_sec.checked_add(secs))?; |
a7813a04 XL |
102 | |
103 | // Nano calculations can't overflow because nanos are <1B which fit | |
104 | // in a u32. | |
04454e1e | 105 | let mut nsec = other.subsec_nanos() + self.tv_nsec as u32; |
a7813a04 XL |
106 | if nsec >= NSEC_PER_SEC as u32 { |
107 | nsec -= NSEC_PER_SEC as u32; | |
a1dfa0c6 | 108 | secs = secs.checked_add(1)?; |
a7813a04 | 109 | } |
04454e1e | 110 | Some(Timespec::new(secs, nsec as i64)) |
a7813a04 XL |
111 | } |
112 | ||
04454e1e | 113 | pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> { |
ea8adc8c XL |
114 | let mut secs = other |
115 | .as_secs() | |
04454e1e | 116 | .try_into() // <- target type would be `i64` |
ea8adc8c | 117 | .ok() |
04454e1e | 118 | .and_then(|secs| self.tv_sec.checked_sub(secs))?; |
a7813a04 XL |
119 | |
120 | // Similar to above, nanos can't overflow. | |
04454e1e | 121 | let mut nsec = self.tv_nsec as i32 - other.subsec_nanos() as i32; |
a7813a04 XL |
122 | if nsec < 0 { |
123 | nsec += NSEC_PER_SEC as i32; | |
0731742a | 124 | secs = secs.checked_sub(1)?; |
a7813a04 | 125 | } |
04454e1e | 126 | Some(Timespec::new(secs, nsec as i64)) |
a7813a04 | 127 | } |
a7813a04 | 128 | |
04454e1e FG |
129 | #[allow(dead_code)] |
130 | pub fn to_timespec(&self) -> Option<libc::timespec> { | |
131 | Some(libc::timespec { | |
132 | tv_sec: self.tv_sec.try_into().ok()?, | |
133 | tv_nsec: self.tv_nsec.try_into().ok()?, | |
134 | }) | |
a7813a04 XL |
135 | } |
136 | } | |
137 | ||
04454e1e FG |
138 | impl From<libc::timespec> for Timespec { |
139 | fn from(t: libc::timespec) -> Timespec { | |
140 | Timespec::new(t.tv_sec as i64, t.tv_nsec as i64) | |
ff7c6d11 XL |
141 | } |
142 | } | |
143 | ||
85aaf69f SL |
144 | #[cfg(any(target_os = "macos", target_os = "ios"))] |
145 | mod inner { | |
29967ef6 | 146 | use crate::sync::atomic::{AtomicU64, Ordering}; |
532ac7d7 XL |
147 | use crate::sys::cvt; |
148 | use crate::sys_common::mul_div_u64; | |
149 | use crate::time::Duration; | |
85aaf69f | 150 | |
04454e1e | 151 | use super::{SystemTime, Timespec, NSEC_PER_SEC}; |
92a42be0 | 152 | |
ff7c6d11 | 153 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] |
92a42be0 | 154 | pub struct Instant { |
dfeec247 | 155 | t: u64, |
85aaf69f SL |
156 | } |
157 | ||
416331ca XL |
158 | #[repr(C)] |
159 | #[derive(Copy, Clone)] | |
160 | struct mach_timebase_info { | |
161 | numer: u32, | |
162 | denom: u32, | |
163 | } | |
164 | type mach_timebase_info_t = *mut mach_timebase_info; | |
165 | type kern_return_t = libc::c_int; | |
166 | ||
92a42be0 SL |
167 | impl Instant { |
168 | pub fn now() -> Instant { | |
416331ca XL |
169 | extern "C" { |
170 | fn mach_absolute_time() -> u64; | |
171 | } | |
172 | Instant { t: unsafe { mach_absolute_time() } } | |
92a42be0 SL |
173 | } |
174 | ||
532ac7d7 XL |
175 | pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { |
176 | let diff = self.t.checked_sub(other.t)?; | |
92a42be0 | 177 | let info = info(); |
92a42be0 | 178 | let nanos = mul_div_u64(diff, info.numer as u64, info.denom as u64); |
532ac7d7 | 179 | Some(Duration::new(nanos / NSEC_PER_SEC, (nanos % NSEC_PER_SEC) as u32)) |
92a42be0 SL |
180 | } |
181 | ||
0731742a | 182 | pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { |
dfeec247 | 183 | Some(Instant { t: self.t.checked_add(checked_dur2intervals(other)?)? }) |
92a42be0 SL |
184 | } |
185 | ||
0731742a | 186 | pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { |
dfeec247 | 187 | Some(Instant { t: self.t.checked_sub(checked_dur2intervals(other)?)? }) |
85aaf69f | 188 | } |
85aaf69f SL |
189 | } |
190 | ||
92a42be0 SL |
191 | impl SystemTime { |
192 | pub fn now() -> SystemTime { | |
532ac7d7 | 193 | use crate::ptr; |
5bcae85e | 194 | |
dfeec247 XL |
195 | let mut s = libc::timeval { tv_sec: 0, tv_usec: 0 }; |
196 | cvt(unsafe { libc::gettimeofday(&mut s, ptr::null_mut()) }).unwrap(); | |
197 | return SystemTime::from(s); | |
92a42be0 | 198 | } |
04454e1e | 199 | } |
92a42be0 | 200 | |
04454e1e FG |
201 | impl From<libc::timeval> for Timespec { |
202 | fn from(t: libc::timeval) -> Timespec { | |
203 | Timespec::new(t.tv_sec as i64, 1000 * t.tv_usec as i64) | |
92a42be0 SL |
204 | } |
205 | } | |
206 | ||
7453a54e SL |
207 | impl From<libc::timeval> for SystemTime { |
208 | fn from(t: libc::timeval) -> SystemTime { | |
04454e1e | 209 | SystemTime { t: Timespec::from(t) } |
92a42be0 SL |
210 | } |
211 | } | |
212 | ||
0731742a | 213 | fn checked_dur2intervals(dur: &Duration) -> Option<u64> { |
dfeec247 XL |
214 | let nanos = |
215 | dur.as_secs().checked_mul(NSEC_PER_SEC)?.checked_add(dur.subsec_nanos() as u64)?; | |
92a42be0 | 216 | let info = info(); |
0731742a | 217 | Some(mul_div_u64(nanos, info.denom as u64, info.numer as u64)) |
92a42be0 SL |
218 | } |
219 | ||
416331ca | 220 | fn info() -> mach_timebase_info { |
29967ef6 XL |
221 | // INFO_BITS conceptually is an `Option<mach_timebase_info>`. We can do |
222 | // this in 64 bits because we know 0 is never a valid value for the | |
223 | // `denom` field. | |
224 | // | |
225 | // Encoding this as a single `AtomicU64` allows us to use `Relaxed` | |
5869c6ff | 226 | // operations, as we are only interested in the effects on a single |
29967ef6 XL |
227 | // memory location. |
228 | static INFO_BITS: AtomicU64 = AtomicU64::new(0); | |
229 | ||
230 | // If a previous thread has initialized `INFO_BITS`, use it. | |
231 | let info_bits = INFO_BITS.load(Ordering::Relaxed); | |
232 | if info_bits != 0 { | |
233 | return info_from_bits(info_bits); | |
234 | } | |
532ac7d7 | 235 | |
29967ef6 XL |
236 | // ... otherwise learn for ourselves ... |
237 | extern "C" { | |
238 | fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t; | |
239 | } | |
416331ca | 240 | |
29967ef6 XL |
241 | let mut info = info_from_bits(0); |
242 | unsafe { | |
416331ca | 243 | mach_timebase_info(&mut info); |
85aaf69f | 244 | } |
29967ef6 XL |
245 | INFO_BITS.store(info_to_bits(info), Ordering::Relaxed); |
246 | info | |
247 | } | |
248 | ||
249 | #[inline] | |
250 | fn info_to_bits(info: mach_timebase_info) -> u64 { | |
251 | ((info.denom as u64) << 32) | (info.numer as u64) | |
252 | } | |
253 | ||
254 | #[inline] | |
255 | fn info_from_bits(bits: u64) -> mach_timebase_info { | |
256 | mach_timebase_info { numer: bits as u32, denom: (bits >> 32) as u32 } | |
85aaf69f | 257 | } |
85aaf69f SL |
258 | } |
259 | ||
260 | #[cfg(not(any(target_os = "macos", target_os = "ios")))] | |
261 | mod inner { | |
532ac7d7 | 262 | use crate::fmt; |
04454e1e | 263 | use crate::mem::MaybeUninit; |
532ac7d7 XL |
264 | use crate::sys::cvt; |
265 | use crate::time::Duration; | |
85aaf69f | 266 | |
04454e1e | 267 | use super::{SystemTime, Timespec}; |
85aaf69f | 268 | |
ff7c6d11 | 269 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
92a42be0 SL |
270 | pub struct Instant { |
271 | t: Timespec, | |
272 | } | |
273 | ||
92a42be0 SL |
274 | impl Instant { |
275 | pub fn now() -> Instant { | |
04454e1e | 276 | Instant { t: Timespec::now(libc::CLOCK_MONOTONIC) } |
92a42be0 SL |
277 | } |
278 | ||
532ac7d7 XL |
279 | pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { |
280 | self.t.sub_timespec(&other.t).ok() | |
92a42be0 SL |
281 | } |
282 | ||
0731742a XL |
283 | pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { |
284 | Some(Instant { t: self.t.checked_add_duration(other)? }) | |
92a42be0 SL |
285 | } |
286 | ||
0731742a XL |
287 | pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { |
288 | Some(Instant { t: self.t.checked_sub_duration(other)? }) | |
92a42be0 SL |
289 | } |
290 | } | |
291 | ||
292 | impl fmt::Debug for Instant { | |
532ac7d7 | 293 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
92a42be0 | 294 | f.debug_struct("Instant") |
04454e1e FG |
295 | .field("tv_sec", &self.t.tv_sec) |
296 | .field("tv_nsec", &self.t.tv_nsec) | |
dfeec247 | 297 | .finish() |
92a42be0 SL |
298 | } |
299 | } | |
300 | ||
301 | impl SystemTime { | |
302 | pub fn now() -> SystemTime { | |
04454e1e | 303 | SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) } |
92a42be0 | 304 | } |
85aaf69f SL |
305 | } |
306 | ||
923072b8 | 307 | #[cfg(not(any(target_os = "dragonfly", target_os = "espidf", target_os = "horizon")))] |
54a0048b | 308 | pub type clock_t = libc::c_int; |
923072b8 | 309 | #[cfg(any(target_os = "dragonfly", target_os = "espidf", target_os = "horizon"))] |
54a0048b SL |
310 | pub type clock_t = libc::c_ulong; |
311 | ||
04454e1e FG |
312 | impl Timespec { |
313 | pub fn now(clock: clock_t) -> Timespec { | |
314 | // Try to use 64-bit time in preparation for Y2038. | |
315 | #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32"))] | |
316 | { | |
317 | use crate::sys::weak::weak; | |
318 | ||
319 | // __clock_gettime64 was added to 32-bit arches in glibc 2.34, | |
320 | // and it handles both vDSO calls and ENOSYS fallbacks itself. | |
321 | weak!(fn __clock_gettime64(libc::clockid_t, *mut __timespec64) -> libc::c_int); | |
322 | ||
323 | #[repr(C)] | |
324 | struct __timespec64 { | |
325 | tv_sec: i64, | |
326 | #[cfg(target_endian = "big")] | |
327 | _padding: i32, | |
328 | tv_nsec: i32, | |
329 | #[cfg(target_endian = "little")] | |
330 | _padding: i32, | |
331 | } | |
332 | ||
333 | if let Some(clock_gettime64) = __clock_gettime64.get() { | |
334 | let mut t = MaybeUninit::uninit(); | |
335 | cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap(); | |
336 | let t = unsafe { t.assume_init() }; | |
337 | return Timespec { tv_sec: t.tv_sec, tv_nsec: t.tv_nsec as i64 }; | |
338 | } | |
339 | } | |
340 | ||
341 | let mut t = MaybeUninit::uninit(); | |
342 | cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap(); | |
343 | Timespec::from(unsafe { t.assume_init() }) | |
344 | } | |
85aaf69f SL |
345 | } |
346 | } |