1 use crate::std::fmt; 2 use crate::std::time::Duration; 3 use dlibc; 4 5 pub use self::inner::Instant; 6 7 const NSEC_PER_SEC: u64 = 1_000_000_000; 8 pub const UNIX_EPOCH: SystemTime = SystemTime { 9 t: Timespec::zero(), 10 }; 11 #[allow(dead_code)] // Used for pthread condvar timeouts 12 pub const TIMESPEC_MAX: dlibc::timespec = dlibc::timespec { 13 tv_sec: <dlibc::time_t>::MAX, 14 tv_nsec: 1_000_000_000 - 1, 15 }; 16 17 // This additional constant is only used when calling 18 // `dlibc::pthread_cond_timedwait`. 19 #[cfg(target_os = "nto")] 20 pub(super) const TIMESPEC_MAX_CAPPED: dlibc::timespec = dlibc::timespec { 21 tv_sec: (u64::MAX / NSEC_PER_SEC) as i64, 22 tv_nsec: (u64::MAX % NSEC_PER_SEC) as i64, 23 }; 24 25 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 26 #[repr(transparent)] 27 #[rustc_layout_scalar_valid_range_start(0)] 28 #[rustc_layout_scalar_valid_range_end(999_999_999)] 29 struct Nanoseconds(u32); 30 31 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 32 pub struct SystemTime { 33 pub(in crate::std::sys::unix) t: Timespec, 34 } 35 36 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 37 pub(in crate::std::sys::unix) struct Timespec { 38 tv_sec: i64, 39 tv_nsec: Nanoseconds, 40 } 41 42 impl SystemTime { 43 #[cfg_attr(target_os = "horizon", allow(unused))] 44 pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { 45 SystemTime { 46 t: Timespec::new(tv_sec, tv_nsec), 47 } 48 } 49 50 pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> { 51 self.t.sub_timespec(&other.t) 52 } 53 54 pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { 55 Some(SystemTime { 56 t: self.t.checked_add_duration(other)?, 57 }) 58 } 59 60 pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { 61 Some(SystemTime { 62 t: self.t.checked_sub_duration(other)?, 63 }) 64 } 65 } 66 67 impl From<dlibc::timespec> for SystemTime { 68 fn from(t: dlibc::timespec) -> SystemTime { 69 SystemTime { 70 t: Timespec::from(t), 71 } 72 } 73 } 74 75 impl fmt::Debug for SystemTime { 76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 77 f.debug_struct("SystemTime") 78 .field("tv_sec", &self.t.tv_sec) 79 .field("tv_nsec", &self.t.tv_nsec.0) 80 .finish() 81 } 82 } 83 84 impl Timespec { 85 pub const fn zero() -> Timespec { 86 Timespec::new(0, 0) 87 } 88 89 const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { 90 assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); 91 // SAFETY: The assert above checks tv_nsec is within the valid range 92 Timespec { 93 tv_sec, 94 tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) }, 95 } 96 } 97 98 pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { 99 if self >= other { 100 // NOTE(eddyb) two aspects of this `if`-`else` are required for LLVM 101 // to optimize it into a branchless form (see also #75545): 102 // 103 // 1. `self.tv_sec - other.tv_sec` shows up as a common expression 104 // in both branches, i.e. the `else` must have its `- 1` 105 // subtraction after the common one, not interleaved with it 106 // (it used to be `self.tv_sec - 1 - other.tv_sec`) 107 // 108 // 2. the `Duration::new` call (or any other additional complexity) 109 // is outside of the `if`-`else`, not duplicated in both branches 110 // 111 // Ideally this code could be rearranged such that it more 112 // directly expresses the lower-cost behavior we want from it. 113 let (secs, nsec) = if self.tv_nsec.0 >= other.tv_nsec.0 { 114 ( 115 (self.tv_sec - other.tv_sec) as u64, 116 self.tv_nsec.0 - other.tv_nsec.0, 117 ) 118 } else { 119 ( 120 (self.tv_sec - other.tv_sec - 1) as u64, 121 self.tv_nsec.0 + (NSEC_PER_SEC as u32) - other.tv_nsec.0, 122 ) 123 }; 124 125 Ok(Duration::new(secs, nsec)) 126 } else { 127 match other.sub_timespec(self) { 128 Ok(d) => Err(d), 129 Err(d) => Ok(d), 130 } 131 } 132 } 133 134 pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> { 135 let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?; 136 137 // Nano calculations can't overflow because nanos are <1B which fit 138 // in a u32. 139 let mut nsec = other.subsec_nanos() + self.tv_nsec.0; 140 if nsec >= NSEC_PER_SEC as u32 { 141 nsec -= NSEC_PER_SEC as u32; 142 secs = secs.checked_add(1)?; 143 } 144 Some(Timespec::new(secs, nsec.into())) 145 } 146 147 pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> { 148 let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?; 149 150 // Similar to above, nanos can't overflow. 151 let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32; 152 if nsec < 0 { 153 nsec += NSEC_PER_SEC as i32; 154 secs = secs.checked_sub(1)?; 155 } 156 Some(Timespec::new(secs, nsec.into())) 157 } 158 159 #[allow(dead_code)] 160 pub fn to_timespec(&self) -> Option<dlibc::timespec> { 161 Some(dlibc::timespec { 162 tv_sec: self.tv_sec.try_into().ok()?, 163 tv_nsec: self.tv_nsec.0.try_into().ok()?, 164 }) 165 } 166 167 // On QNX Neutrino, the maximum timespec for e.g. pthread_cond_timedwait 168 // is 2^64 nanoseconds 169 #[cfg(target_os = "nto")] 170 pub(super) fn to_timespec_capped(&self) -> Option<dlibc::timespec> { 171 // Check if timeout in nanoseconds would fit into an u64 172 if (self.tv_nsec.0 as u64) 173 .checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?) 174 .is_none() 175 { 176 return None; 177 } 178 self.to_timespec() 179 } 180 181 #[cfg(all( 182 target_os = "linux", 183 target_env = "gnu", 184 target_pointer_width = "32", 185 not(target_arch = "riscv32") 186 ))] 187 pub fn to_timespec64(&self) -> __timespec64 { 188 __timespec64::new(self.tv_sec, self.tv_nsec.0 as _) 189 } 190 } 191 192 impl From<dlibc::timespec> for Timespec { 193 fn from(t: dlibc::timespec) -> Timespec { 194 Timespec::new(t.tv_sec as i64, t.tv_nsec as i64) 195 } 196 } 197 198 #[cfg(all( 199 target_os = "linux", 200 target_env = "gnu", 201 target_pointer_width = "32", 202 not(target_arch = "riscv32") 203 ))] 204 #[repr(C)] 205 pub(in crate::std::sys::unix) struct __timespec64 { 206 pub(in crate::std::sys::unix) tv_sec: i64, 207 #[cfg(target_endian = "big")] 208 _padding: i32, 209 pub(in crate::std::sys::unix) tv_nsec: i32, 210 #[cfg(target_endian = "little")] 211 _padding: i32, 212 } 213 214 #[cfg(all( 215 target_os = "linux", 216 target_env = "gnu", 217 target_pointer_width = "32", 218 not(target_arch = "riscv32") 219 ))] 220 impl __timespec64 { 221 pub(in crate::std::sys::unix) fn new(tv_sec: i64, tv_nsec: i32) -> Self { 222 Self { 223 tv_sec, 224 tv_nsec, 225 _padding: 0, 226 } 227 } 228 } 229 230 #[cfg(all( 231 target_os = "linux", 232 target_env = "gnu", 233 target_pointer_width = "32", 234 not(target_arch = "riscv32") 235 ))] 236 impl From<__timespec64> for Timespec { 237 fn from(t: __timespec64) -> Timespec { 238 Timespec::new(t.tv_sec, t.tv_nsec.into()) 239 } 240 } 241 242 #[cfg(any( 243 all(target_os = "macos", any(not(target_arch = "aarch64"))), 244 target_os = "ios", 245 target_os = "watchos", 246 target_os = "tvos" 247 ))] 248 mod inner { 249 use crate::std::sync::atomic::{AtomicU64, Ordering}; 250 use crate::std::sys::cvt; 251 use crate::std::sys_common::mul_div_u64; 252 use crate::std::time::Duration; 253 254 use super::{SystemTime, Timespec, NSEC_PER_SEC}; 255 256 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] 257 pub struct Instant { 258 t: u64, 259 } 260 261 #[repr(C)] 262 #[derive(Copy, Clone)] 263 struct mach_timebase_info { 264 numer: u32, 265 denom: u32, 266 } 267 type mach_timebase_info_t = *mut mach_timebase_info; 268 type kern_return_t = dlibc::c_int; 269 270 impl Instant { 271 pub fn now() -> Instant { 272 extern "C" { 273 fn mach_absolute_time() -> u64; 274 } 275 Instant { 276 t: unsafe { mach_absolute_time() }, 277 } 278 } 279 280 pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { 281 let diff = self.t.checked_sub(other.t)?; 282 let info = info(); 283 let nanos = mul_div_u64(diff, info.numer as u64, info.denom as u64); 284 Some(Duration::new( 285 nanos / NSEC_PER_SEC, 286 (nanos % NSEC_PER_SEC) as u32, 287 )) 288 } 289 290 pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { 291 Some(Instant { 292 t: self.t.checked_add(checked_dur2intervals(other)?)?, 293 }) 294 } 295 296 pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { 297 Some(Instant { 298 t: self.t.checked_sub(checked_dur2intervals(other)?)?, 299 }) 300 } 301 } 302 303 impl SystemTime { 304 pub fn now() -> SystemTime { 305 use crate::std::ptr; 306 307 let mut s = dlibc::timeval { 308 tv_sec: 0, 309 tv_usec: 0, 310 }; 311 cvt(unsafe { dlibc::gettimeofday(&mut s, ptr::null_mut()) }).unwrap(); 312 return SystemTime::from(s); 313 } 314 } 315 316 impl From<dlibc::timeval> for Timespec { 317 fn from(t: dlibc::timeval) -> Timespec { 318 Timespec::new(t.tv_sec as i64, 1000 * t.tv_usec as i64) 319 } 320 } 321 322 impl From<dlibc::timeval> for SystemTime { 323 fn from(t: dlibc::timeval) -> SystemTime { 324 SystemTime { 325 t: Timespec::from(t), 326 } 327 } 328 } 329 330 fn checked_dur2intervals(dur: &Duration) -> Option<u64> { 331 let nanos = dur 332 .as_secs() 333 .checked_mul(NSEC_PER_SEC)? 334 .checked_add(dur.subsec_nanos() as u64)?; 335 let info = info(); 336 Some(mul_div_u64(nanos, info.denom as u64, info.numer as u64)) 337 } 338 339 fn info() -> mach_timebase_info { 340 // INFO_BITS conceptually is an `Option<mach_timebase_info>`. We can do 341 // this in 64 bits because we know 0 is never a valid value for the 342 // `denom` field. 343 // 344 // Encoding this as a single `AtomicU64` allows us to use `Relaxed` 345 // operations, as we are only interested in the effects on a single 346 // memory location. 347 static INFO_BITS: AtomicU64 = AtomicU64::new(0); 348 349 // If a previous thread has initialized `INFO_BITS`, use it. 350 let info_bits = INFO_BITS.load(Ordering::Relaxed); 351 if info_bits != 0 { 352 return info_from_bits(info_bits); 353 } 354 355 // ... otherwise learn for ourselves ... 356 extern "C" { 357 fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t; 358 } 359 360 let mut info = info_from_bits(0); 361 unsafe { 362 mach_timebase_info(&mut info); 363 } 364 INFO_BITS.store(info_to_bits(info), Ordering::Relaxed); 365 info 366 } 367 368 #[inline] 369 fn info_to_bits(info: mach_timebase_info) -> u64 { 370 ((info.denom as u64) << 32) | (info.numer as u64) 371 } 372 373 #[inline] 374 fn info_from_bits(bits: u64) -> mach_timebase_info { 375 mach_timebase_info { 376 numer: bits as u32, 377 denom: (bits >> 32) as u32, 378 } 379 } 380 } 381 382 #[cfg(not(any( 383 all(target_os = "macos", any(not(target_arch = "aarch64"))), 384 target_os = "ios", 385 target_os = "watchos", 386 target_os = "tvos" 387 )))] 388 mod inner { 389 use crate::std::fmt; 390 use crate::std::mem::MaybeUninit; 391 use crate::std::sys::cvt; 392 use crate::std::time::Duration; 393 use dlibc; 394 395 use super::{SystemTime, Timespec}; 396 397 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 398 pub struct Instant { 399 t: Timespec, 400 } 401 402 impl Instant { 403 pub fn now() -> Instant { 404 #[cfg(target_os = "macos")] 405 const clock_id: dlibc::clockid_t = dlibc::CLOCK_UPTIME_RAW; 406 #[cfg(not(target_os = "macos"))] 407 const clock_id: dlibc::clockid_t = dlibc::CLOCK_MONOTONIC; 408 Instant { 409 t: Timespec::now(clock_id), 410 } 411 } 412 413 pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { 414 self.t.sub_timespec(&other.t).ok() 415 } 416 417 pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { 418 Some(Instant { 419 t: self.t.checked_add_duration(other)?, 420 }) 421 } 422 423 pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { 424 Some(Instant { 425 t: self.t.checked_sub_duration(other)?, 426 }) 427 } 428 } 429 430 impl fmt::Debug for Instant { 431 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 432 f.debug_struct("Instant") 433 .field("tv_sec", &self.t.tv_sec) 434 .field("tv_nsec", &self.t.tv_nsec.0) 435 .finish() 436 } 437 } 438 439 impl SystemTime { 440 pub fn now() -> SystemTime { 441 SystemTime { 442 t: Timespec::now(dlibc::CLOCK_REALTIME), 443 } 444 } 445 } 446 447 impl Timespec { 448 pub fn now(clock: dlibc::clockid_t) -> Timespec { 449 // Try to use 64-bit time in preparation for Y2038. 450 #[cfg(all( 451 target_os = "linux", 452 target_env = "gnu", 453 target_pointer_width = "32", 454 not(target_arch = "riscv32") 455 ))] 456 { 457 use crate::std::sys::weak::weak; 458 459 // __clock_gettime64 was added to 32-bit arches in glibc 2.34, 460 // and it handles both vDSO calls and ENOSYS fallbacks itself. 461 weak!(fn __clock_gettime64(dlibc::clockid_t, *mut super::__timespec64) -> dlibc::c_int); 462 463 if let Some(clock_gettime64) = __clock_gettime64.get() { 464 let mut t = MaybeUninit::uninit(); 465 cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap(); 466 return Timespec::from(unsafe { t.assume_init() }); 467 } 468 } 469 470 let mut t = MaybeUninit::uninit(); 471 cvt(unsafe { dlibc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap(); 472 Timespec::from(unsafe { t.assume_init() }) 473 } 474 } 475 } 476