1 | use crate::fmt; |
2 | use crate::time::Duration; |
3 | |
4 | const NSEC_PER_SEC: u64 = 1_000_000_000; |
5 | pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; |
6 | #[allow (dead_code)] // Used for pthread condvar timeouts |
7 | pub const TIMESPEC_MAX: libc::timespec = |
8 | libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 }; |
9 | |
10 | // This additional constant is only used when calling |
11 | // `libc::pthread_cond_timedwait`. |
12 | #[cfg (target_os = "nto" )] |
13 | pub(super) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec { |
14 | tv_sec: (u64::MAX / NSEC_PER_SEC) as i64, |
15 | tv_nsec: (u64::MAX % NSEC_PER_SEC) as i64, |
16 | }; |
17 | |
18 | #[derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
19 | #[repr (transparent)] |
20 | #[rustc_layout_scalar_valid_range_start (0)] |
21 | #[rustc_layout_scalar_valid_range_end (999_999_999)] |
22 | struct Nanoseconds(u32); |
23 | |
24 | #[derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
25 | pub struct SystemTime { |
26 | pub(crate) t: Timespec, |
27 | } |
28 | |
29 | #[derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
30 | pub(crate) struct Timespec { |
31 | tv_sec: i64, |
32 | tv_nsec: Nanoseconds, |
33 | } |
34 | |
35 | impl SystemTime { |
36 | #[cfg_attr (any(target_os = "horizon" , target_os = "hurd" ), allow(unused))] |
37 | pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { |
38 | SystemTime { t: Timespec::new(tv_sec, tv_nsec) } |
39 | } |
40 | |
41 | pub fn now() -> SystemTime { |
42 | SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) } |
43 | } |
44 | |
45 | pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> { |
46 | self.t.sub_timespec(&other.t) |
47 | } |
48 | |
49 | pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { |
50 | Some(SystemTime { t: self.t.checked_add_duration(other)? }) |
51 | } |
52 | |
53 | pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { |
54 | Some(SystemTime { t: self.t.checked_sub_duration(other)? }) |
55 | } |
56 | } |
57 | |
58 | impl From<libc::timespec> for SystemTime { |
59 | fn from(t: libc::timespec) -> SystemTime { |
60 | SystemTime { t: Timespec::from(t) } |
61 | } |
62 | } |
63 | |
64 | impl fmt::Debug for SystemTime { |
65 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
66 | f&mut DebugStruct<'_, '_>.debug_struct("SystemTime" ) |
67 | .field("tv_sec" , &self.t.tv_sec) |
68 | .field(name:"tv_nsec" , &self.t.tv_nsec.0) |
69 | .finish() |
70 | } |
71 | } |
72 | |
73 | impl Timespec { |
74 | pub const fn zero() -> Timespec { |
75 | Timespec::new(0, 0) |
76 | } |
77 | |
78 | const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { |
79 | // On Apple OS, dates before epoch are represented differently than on other |
80 | // Unix platforms: e.g. 1/10th of a second before epoch is represented as `seconds=-1` |
81 | // and `nanoseconds=100_000_000` on other platforms, but is `seconds=0` and |
82 | // `nanoseconds=-900_000_000` on Apple OS. |
83 | // |
84 | // To compensate, we first detect this special case by checking if both |
85 | // seconds and nanoseconds are in range, and then correct the value for seconds |
86 | // and nanoseconds to match the common unix representation. |
87 | // |
88 | // Please note that Apple OS nonetheless accepts the standard unix format when |
89 | // setting file times, which makes this compensation round-trippable and generally |
90 | // transparent. |
91 | #[cfg (any( |
92 | target_os = "macos" , |
93 | target_os = "ios" , |
94 | target_os = "tvos" , |
95 | target_os = "watchos" |
96 | ))] |
97 | let (tv_sec, tv_nsec) = |
98 | if (tv_sec <= 0 && tv_sec > i64::MIN) && (tv_nsec < 0 && tv_nsec > -1_000_000_000) { |
99 | (tv_sec - 1, tv_nsec + 1_000_000_000) |
100 | } else { |
101 | (tv_sec, tv_nsec) |
102 | }; |
103 | assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); |
104 | // SAFETY: The assert above checks tv_nsec is within the valid range |
105 | Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } } |
106 | } |
107 | |
108 | pub fn now(clock: libc::clockid_t) -> Timespec { |
109 | use crate::mem::MaybeUninit; |
110 | use crate::sys::cvt; |
111 | |
112 | // Try to use 64-bit time in preparation for Y2038. |
113 | #[cfg (all( |
114 | target_os = "linux" , |
115 | target_env = "gnu" , |
116 | target_pointer_width = "32" , |
117 | not(target_arch = "riscv32" ) |
118 | ))] |
119 | { |
120 | use crate::sys::weak::weak; |
121 | |
122 | // __clock_gettime64 was added to 32-bit arches in glibc 2.34, |
123 | // and it handles both vDSO calls and ENOSYS fallbacks itself. |
124 | weak!(fn __clock_gettime64(libc::clockid_t, *mut __timespec64) -> libc::c_int); |
125 | |
126 | if let Some(clock_gettime64) = __clock_gettime64.get() { |
127 | let mut t = MaybeUninit::uninit(); |
128 | cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap(); |
129 | return Timespec::from(unsafe { t.assume_init() }); |
130 | } |
131 | } |
132 | |
133 | let mut t = MaybeUninit::uninit(); |
134 | cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap(); |
135 | Timespec::from(unsafe { t.assume_init() }) |
136 | } |
137 | |
138 | pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { |
139 | if self >= other { |
140 | // NOTE(eddyb) two aspects of this `if`-`else` are required for LLVM |
141 | // to optimize it into a branchless form (see also #75545): |
142 | // |
143 | // 1. `self.tv_sec - other.tv_sec` shows up as a common expression |
144 | // in both branches, i.e. the `else` must have its `- 1` |
145 | // subtraction after the common one, not interleaved with it |
146 | // (it used to be `self.tv_sec - 1 - other.tv_sec`) |
147 | // |
148 | // 2. the `Duration::new` call (or any other additional complexity) |
149 | // is outside of the `if`-`else`, not duplicated in both branches |
150 | // |
151 | // Ideally this code could be rearranged such that it more |
152 | // directly expresses the lower-cost behavior we want from it. |
153 | let (secs, nsec) = if self.tv_nsec.0 >= other.tv_nsec.0 { |
154 | ((self.tv_sec - other.tv_sec) as u64, self.tv_nsec.0 - other.tv_nsec.0) |
155 | } else { |
156 | ( |
157 | (self.tv_sec - other.tv_sec - 1) as u64, |
158 | self.tv_nsec.0 + (NSEC_PER_SEC as u32) - other.tv_nsec.0, |
159 | ) |
160 | }; |
161 | |
162 | Ok(Duration::new(secs, nsec)) |
163 | } else { |
164 | match other.sub_timespec(self) { |
165 | Ok(d) => Err(d), |
166 | Err(d) => Ok(d), |
167 | } |
168 | } |
169 | } |
170 | |
171 | pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> { |
172 | let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?; |
173 | |
174 | // Nano calculations can't overflow because nanos are <1B which fit |
175 | // in a u32. |
176 | let mut nsec = other.subsec_nanos() + self.tv_nsec.0; |
177 | if nsec >= NSEC_PER_SEC as u32 { |
178 | nsec -= NSEC_PER_SEC as u32; |
179 | secs = secs.checked_add(1)?; |
180 | } |
181 | Some(Timespec::new(secs, nsec.into())) |
182 | } |
183 | |
184 | pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> { |
185 | let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?; |
186 | |
187 | // Similar to above, nanos can't overflow. |
188 | let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32; |
189 | if nsec < 0 { |
190 | nsec += NSEC_PER_SEC as i32; |
191 | secs = secs.checked_sub(1)?; |
192 | } |
193 | Some(Timespec::new(secs, nsec.into())) |
194 | } |
195 | |
196 | #[allow (dead_code)] |
197 | pub fn to_timespec(&self) -> Option<libc::timespec> { |
198 | Some(libc::timespec { |
199 | tv_sec: self.tv_sec.try_into().ok()?, |
200 | tv_nsec: self.tv_nsec.0.try_into().ok()?, |
201 | }) |
202 | } |
203 | |
204 | // On QNX Neutrino, the maximum timespec for e.g. pthread_cond_timedwait |
205 | // is 2^64 nanoseconds |
206 | #[cfg (target_os = "nto" )] |
207 | pub(super) fn to_timespec_capped(&self) -> Option<libc::timespec> { |
208 | // Check if timeout in nanoseconds would fit into an u64 |
209 | if (self.tv_nsec.0 as u64) |
210 | .checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?) |
211 | .is_none() |
212 | { |
213 | return None; |
214 | } |
215 | self.to_timespec() |
216 | } |
217 | |
218 | #[cfg (all( |
219 | target_os = "linux" , |
220 | target_env = "gnu" , |
221 | target_pointer_width = "32" , |
222 | not(target_arch = "riscv32" ) |
223 | ))] |
224 | pub fn to_timespec64(&self) -> __timespec64 { |
225 | __timespec64::new(self.tv_sec, self.tv_nsec.0 as _) |
226 | } |
227 | } |
228 | |
229 | impl From<libc::timespec> for Timespec { |
230 | fn from(t: libc::timespec) -> Timespec { |
231 | Timespec::new(t.tv_sec as i64, t.tv_nsec as i64) |
232 | } |
233 | } |
234 | |
235 | #[cfg (all( |
236 | target_os = "linux" , |
237 | target_env = "gnu" , |
238 | target_pointer_width = "32" , |
239 | not(target_arch = "riscv32" ) |
240 | ))] |
241 | #[repr (C)] |
242 | pub(crate) struct __timespec64 { |
243 | pub(crate) tv_sec: i64, |
244 | #[cfg (target_endian = "big" )] |
245 | _padding: i32, |
246 | pub(crate) tv_nsec: i32, |
247 | #[cfg (target_endian = "little" )] |
248 | _padding: i32, |
249 | } |
250 | |
251 | #[cfg (all( |
252 | target_os = "linux" , |
253 | target_env = "gnu" , |
254 | target_pointer_width = "32" , |
255 | not(target_arch = "riscv32" ) |
256 | ))] |
257 | impl __timespec64 { |
258 | pub(crate) fn new(tv_sec: i64, tv_nsec: i32) -> Self { |
259 | Self { tv_sec, tv_nsec, _padding: 0 } |
260 | } |
261 | } |
262 | |
263 | #[cfg (all( |
264 | target_os = "linux" , |
265 | target_env = "gnu" , |
266 | target_pointer_width = "32" , |
267 | not(target_arch = "riscv32" ) |
268 | ))] |
269 | impl From<__timespec64> for Timespec { |
270 | fn from(t: __timespec64) -> Timespec { |
271 | Timespec::new(t.tv_sec, t.tv_nsec.into()) |
272 | } |
273 | } |
274 | |
275 | #[derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
276 | pub struct Instant { |
277 | t: Timespec, |
278 | } |
279 | |
280 | impl Instant { |
281 | pub fn now() -> Instant { |
282 | // https://www.manpagez.com/man/3/clock_gettime/ |
283 | // |
284 | // CLOCK_UPTIME_RAW clock that increments monotonically, in the same man- |
285 | // ner as CLOCK_MONOTONIC_RAW, but that does not incre- |
286 | // ment while the system is asleep. The returned value |
287 | // is identical to the result of mach_absolute_time() |
288 | // after the appropriate mach_timebase conversion is |
289 | // applied. |
290 | // |
291 | // Instant on macos was historically implemented using mach_absolute_time; |
292 | // we preserve this value domain out of an abundance of caution. |
293 | #[cfg (any( |
294 | target_os = "macos" , |
295 | target_os = "ios" , |
296 | target_os = "watchos" , |
297 | target_os = "tvos" |
298 | ))] |
299 | const clock_id: libc::clockid_t = libc::CLOCK_UPTIME_RAW; |
300 | #[cfg (not(any( |
301 | target_os = "macos" , |
302 | target_os = "ios" , |
303 | target_os = "watchos" , |
304 | target_os = "tvos" |
305 | )))] |
306 | const clock_id: libc::clockid_t = libc::CLOCK_MONOTONIC; |
307 | Instant { t: Timespec::now(clock_id) } |
308 | } |
309 | |
310 | pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { |
311 | self.t.sub_timespec(&other.t).ok() |
312 | } |
313 | |
314 | pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { |
315 | Some(Instant { t: self.t.checked_add_duration(other)? }) |
316 | } |
317 | |
318 | pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { |
319 | Some(Instant { t: self.t.checked_sub_duration(other)? }) |
320 | } |
321 | } |
322 | |
323 | impl fmt::Debug for Instant { |
324 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
325 | f&mut DebugStruct<'_, '_>.debug_struct("Instant" ) |
326 | .field("tv_sec" , &self.t.tv_sec) |
327 | .field(name:"tv_nsec" , &self.t.tv_nsec.0) |
328 | .finish() |
329 | } |
330 | } |
331 | |