From bee923f0df0e4a568811e24dc74af77c464d10f3 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 9 Mar 2022 12:25:46 -0800 Subject: [PATCH 1/4] unix: always use 64-bit Timespec --- library/std/src/sys/unix/futex.rs | 9 +- library/std/src/sys/unix/thread_parker.rs | 3 +- library/std/src/sys/unix/time.rs | 120 +++++++++++----------- 3 files changed, 65 insertions(+), 67 deletions(-) diff --git a/library/std/src/sys/unix/futex.rs b/library/std/src/sys/unix/futex.rs index 678c6f0d6ea..c1966d67078 100644 --- a/library/std/src/sys/unix/futex.rs +++ b/library/std/src/sys/unix/futex.rs @@ -24,8 +24,9 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - // Calculate the timeout as an absolute timespec. // // Overflows are rounded up to an infinite timeout (None). - let timespec = - timeout.and_then(|d| Some(Timespec::now(libc::CLOCK_MONOTONIC).checked_add_duration(&d)?)); + let timespec = timeout + .and_then(|d| Some(Timespec::now(libc::CLOCK_MONOTONIC).checked_add_duration(&d)?)) + .and_then(|t| t.to_timespec()); loop { // No need to wait if the value already changed. @@ -41,7 +42,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - // identical. It supports absolute timeouts through a flag // in the _umtx_time struct. let umtx_timeout = timespec.map(|t| libc::_umtx_time { - _timeout: t.t, + _timeout: t, _flags: libc::UMTX_ABSTIME, _clockid: libc::CLOCK_MONOTONIC as u32, }); @@ -62,7 +63,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - futex as *const AtomicU32, libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG, expected, - timespec.as_ref().map_or(null(), |t| &t.t as *const libc::timespec), + timespec.as_ref().map_or(null(), |t| t as *const libc::timespec), null::(), // This argument is unused for FUTEX_WAIT_BITSET. !0u32, // A full bitmask, to make it behave like a regular FUTEX_WAIT. ) diff --git a/library/std/src/sys/unix/thread_parker.rs b/library/std/src/sys/unix/thread_parker.rs index cf37c01598b..30ed2ec7f54 100644 --- a/library/std/src/sys/unix/thread_parker.rs +++ b/library/std/src/sys/unix/thread_parker.rs @@ -79,7 +79,8 @@ unsafe fn wait_timeout( (Timespec::now(libc::CLOCK_MONOTONIC), dur) }; - let timeout = now.checked_add_duration(&dur).map(|t| t.t).unwrap_or(TIMESPEC_MAX); + let timeout = + now.checked_add_duration(&dur).and_then(|t| t.to_timespec()).unwrap_or(TIMESPEC_MAX); let r = libc::pthread_cond_timedwait(cond, lock, &timeout); debug_assert!(r == libc::ETIMEDOUT || r == 0); } diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index d43ceec9c8a..6cf06848399 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -1,21 +1,23 @@ -use crate::cmp::Ordering; use crate::time::Duration; -use core::hash::{Hash, Hasher}; - pub use self::inner::{Instant, SystemTime, UNIX_EPOCH}; use crate::convert::TryInto; const NSEC_PER_SEC: u64 = 1_000_000_000; -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub(in crate::sys::unix) struct Timespec { - pub t: libc::timespec, + tv_sec: i64, + tv_nsec: i64, } impl Timespec { const fn zero() -> Timespec { - Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } } + Timespec { tv_sec: 0, tv_nsec: 0 } + } + + fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { + Timespec { tv_sec, tv_nsec } } pub fn sub_timespec(&self, other: &Timespec) -> Result { @@ -23,22 +25,22 @@ impl Timespec { // NOTE(eddyb) two aspects of this `if`-`else` are required for LLVM // to optimize it into a branchless form (see also #75545): // - // 1. `self.t.tv_sec - other.t.tv_sec` shows up as a common expression + // 1. `self.tv_sec - other.tv_sec` shows up as a common expression // in both branches, i.e. the `else` must have its `- 1` // subtraction after the common one, not interleaved with it - // (it used to be `self.t.tv_sec - 1 - other.t.tv_sec`) + // (it used to be `self.tv_sec - 1 - other.tv_sec`) // // 2. the `Duration::new` call (or any other additional complexity) // is outside of the `if`-`else`, not duplicated in both branches // // Ideally this code could be rearranged such that it more // directly expresses the lower-cost behavior we want from it. - let (secs, nsec) = if self.t.tv_nsec >= other.t.tv_nsec { - ((self.t.tv_sec - other.t.tv_sec) as u64, (self.t.tv_nsec - other.t.tv_nsec) as u32) + let (secs, nsec) = if self.tv_nsec >= other.tv_nsec { + ((self.tv_sec - other.tv_sec) as u64, (self.tv_nsec - other.tv_nsec) as u32) } else { ( - (self.t.tv_sec - other.t.tv_sec - 1) as u64, - self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.t.tv_nsec as u32, + (self.tv_sec - other.tv_sec - 1) as u64, + self.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.tv_nsec as u32, ) }; @@ -54,63 +56,34 @@ impl Timespec { pub fn checked_add_duration(&self, other: &Duration) -> Option { let mut secs = other .as_secs() - .try_into() // <- target type would be `libc::time_t` + .try_into() // <- target type would be `i64` .ok() - .and_then(|secs| self.t.tv_sec.checked_add(secs))?; + .and_then(|secs| self.tv_sec.checked_add(secs))?; // Nano calculations can't overflow because nanos are <1B which fit // in a u32. - let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; + let mut nsec = other.subsec_nanos() + self.tv_nsec as u32; if nsec >= NSEC_PER_SEC as u32 { nsec -= NSEC_PER_SEC as u32; secs = secs.checked_add(1)?; } - Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } }) + Some(Timespec::new(secs, nsec as i64)) } pub fn checked_sub_duration(&self, other: &Duration) -> Option { let mut secs = other .as_secs() - .try_into() // <- target type would be `libc::time_t` + .try_into() // <- target type would be `i64` .ok() - .and_then(|secs| self.t.tv_sec.checked_sub(secs))?; + .and_then(|secs| self.tv_sec.checked_sub(secs))?; // Similar to above, nanos can't overflow. - let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; + let mut nsec = self.tv_nsec as i32 - other.subsec_nanos() as i32; if nsec < 0 { nsec += NSEC_PER_SEC as i32; secs = secs.checked_sub(1)?; } - Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } }) - } -} - -impl PartialEq for Timespec { - fn eq(&self, other: &Timespec) -> bool { - self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec - } -} - -impl Eq for Timespec {} - -impl PartialOrd for Timespec { - fn partial_cmp(&self, other: &Timespec) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Timespec { - fn cmp(&self, other: &Timespec) -> Ordering { - let me = (self.t.tv_sec, self.t.tv_nsec); - let other = (other.t.tv_sec, other.t.tv_nsec); - me.cmp(&other) - } -} - -impl Hash for Timespec { - fn hash(&self, state: &mut H) { - self.t.tv_sec.hash(state); - self.t.tv_nsec.hash(state); + Some(Timespec::new(secs, nsec as i64)) } } @@ -192,26 +165,35 @@ mod inner { } } + impl From for Timespec { + fn from(t: libc::timeval) -> Timespec { + Timespec::new(t.tv_sec as i64, 1000 * t.tv_usec as i64) + } + } + impl From for SystemTime { fn from(t: libc::timeval) -> SystemTime { - SystemTime::from(libc::timespec { - tv_sec: t.tv_sec, - tv_nsec: (t.tv_usec * 1000) as libc::c_long, - }) + SystemTime { t: Timespec::from(t) } + } + } + + impl From for Timespec { + fn from(t: libc::timespec) -> Timespec { + Timespec::new(t.tv_sec as i64, t.tv_nsec as i64) } } impl From for SystemTime { fn from(t: libc::timespec) -> SystemTime { - SystemTime { t: Timespec { t } } + SystemTime { t: Timespec::from(t) } } } impl fmt::Debug for SystemTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SystemTime") - .field("tv_sec", &self.t.t.tv_sec) - .field("tv_nsec", &self.t.t.tv_nsec) + .field("tv_sec", &self.t.tv_sec) + .field("tv_nsec", &self.t.tv_nsec) .finish() } } @@ -305,8 +287,8 @@ mod inner { impl fmt::Debug for Instant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Instant") - .field("tv_sec", &self.t.t.tv_sec) - .field("tv_nsec", &self.t.t.tv_nsec) + .field("tv_sec", &self.t.tv_sec) + .field("tv_nsec", &self.t.tv_nsec) .finish() } } @@ -329,17 +311,23 @@ mod inner { } } + impl From for Timespec { + fn from(t: libc::timespec) -> Timespec { + Timespec::new(t.tv_sec as i64, t.tv_nsec as i64) + } + } + impl From for SystemTime { fn from(t: libc::timespec) -> SystemTime { - SystemTime { t: Timespec { t } } + SystemTime { t: Timespec::from(t) } } } impl fmt::Debug for SystemTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SystemTime") - .field("tv_sec", &self.t.t.tv_sec) - .field("tv_nsec", &self.t.t.tv_nsec) + .field("tv_sec", &self.t.tv_sec) + .field("tv_nsec", &self.t.tv_nsec) .finish() } } @@ -353,7 +341,15 @@ mod inner { pub fn now(clock: clock_t) -> Timespec { let mut t = MaybeUninit::uninit(); cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap(); - Timespec { t: unsafe { t.assume_init() } } + Timespec::from(unsafe { t.assume_init() }) + } + + pub fn to_timespec(&self) -> Option { + use crate::convert::TryInto; + Some(libc::timespec { + tv_sec: self.tv_sec.try_into().ok()?, + tv_nsec: self.tv_nsec.try_into().ok()?, + }) } } } From 97b49a0cc5bea5845dc89c1598a32d7f9fea985a Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 9 Mar 2022 12:41:47 -0800 Subject: [PATCH 2/4] Use __clock_gettime64 on 32-bit linux-gnu --- library/std/src/sys/unix/time.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index 6cf06848399..20d061206be 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -339,6 +339,33 @@ mod inner { impl Timespec { pub fn now(clock: clock_t) -> Timespec { + // Try to use 64-bit time in preparation for Y2038. + #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32"))] + { + use crate::sys::weak::weak; + + // __clock_gettime64 was added to 32-bit arches in glibc 2.34, + // and it handles both vDSO calls and ENOSYS fallbacks itself. + weak!(fn __clock_gettime64(libc::clockid_t, *mut __timespec64) -> libc::c_int); + + #[repr(C)] + struct __timespec64 { + tv_sec: i64, + #[cfg(target_endian = "big")] + _padding: i32, + tv_nsec: i32, + #[cfg(target_endian = "little")] + _padding: i32, + } + + if let Some(clock_gettime64) = __clock_gettime64.get() { + let mut t = MaybeUninit::uninit(); + cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap(); + let t = unsafe { t.assume_init() }; + return Timespec { tv_sec: t.tv_sec, tv_nsec: t.tv_nsec as i64 }; + } + } + let mut t = MaybeUninit::uninit(); cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap(); Timespec::from(unsafe { t.assume_init() }) From fec4818fdb40c82679f57fa7f26fcddc1a874c13 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 9 Mar 2022 17:05:16 -0800 Subject: [PATCH 3/4] Use statx's 64-bit times on 32-bit linux-gnu --- library/std/src/os/linux/fs.rs | 21 +++++- library/std/src/sys/unix/fs.rs | 109 ++++++++++++++++++++----------- library/std/src/sys/unix/time.rs | 8 +++ 3 files changed, 97 insertions(+), 41 deletions(-) diff --git a/library/std/src/os/linux/fs.rs b/library/std/src/os/linux/fs.rs index 9d18ccbeb24..23267c44685 100644 --- a/library/std/src/os/linux/fs.rs +++ b/library/std/src/os/linux/fs.rs @@ -356,19 +356,34 @@ impl MetadataExt for Metadata { self.as_inner().as_inner().st_size as u64 } fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 + let file_attr = self.as_inner(); + #[cfg(all(target_env = "gnu", target_pointer_width = "32"))] + if let Some(atime) = file_attr.stx_atime() { + return atime.tv_sec; + } + file_attr.as_inner().st_atime as i64 } fn st_atime_nsec(&self) -> i64 { self.as_inner().as_inner().st_atime_nsec as i64 } fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 + let file_attr = self.as_inner(); + #[cfg(all(target_env = "gnu", target_pointer_width = "32"))] + if let Some(mtime) = file_attr.stx_mtime() { + return mtime.tv_sec; + } + file_attr.as_inner().st_mtime as i64 } fn st_mtime_nsec(&self) -> i64 { self.as_inner().as_inner().st_mtime_nsec as i64 } fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 + let file_attr = self.as_inner(); + #[cfg(all(target_env = "gnu", target_pointer_width = "32"))] + if let Some(ctime) = file_attr.stx_ctime() { + return ctime.tv_sec; + } + file_attr.as_inner().st_ctime as i64 } fn st_ctime_nsec(&self) -> i64 { self.as_inner().as_inner().st_ctime_nsec as i64 diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 27fc7accdae..61fb2814018 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -113,10 +113,19 @@ cfg_has_statx! {{ // This is needed to check if btime is supported by the filesystem. stx_mask: u32, stx_btime: libc::statx_timestamp, + // With statx, we can overcome 32-bit `time_t` too. + #[cfg(target_pointer_width = "32")] + stx_atime: libc::statx_timestamp, + #[cfg(target_pointer_width = "32")] + stx_ctime: libc::statx_timestamp, + #[cfg(target_pointer_width = "32")] + stx_mtime: libc::statx_timestamp, + } - // We prefer `statx` on Linux if available, which contains file creation time. - // Default `stat64` contains no creation time. + // We prefer `statx` on Linux if available, which contains file creation time, + // as well as 64-bit timestamps of all kinds. + // Default `stat64` contains no creation time and may have 32-bit `time_t`. unsafe fn try_statx( fd: c_int, path: *const c_char, @@ -192,6 +201,13 @@ cfg_has_statx! {{ let extra = StatxExtraFields { stx_mask: buf.stx_mask, stx_btime: buf.stx_btime, + // Store full times to avoid 32-bit `time_t` truncation. + #[cfg(target_pointer_width = "32")] + stx_atime: buf.stx_atime, + #[cfg(target_pointer_width = "32")] + stx_ctime: buf.stx_ctime, + #[cfg(target_pointer_width = "32")] + stx_mtime: buf.stx_mtime, }; Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) })) @@ -310,6 +326,36 @@ cfg_has_statx! {{ fn from_stat64(stat: stat64) -> Self { Self { stat, statx_extra_fields: None } } + + #[cfg(target_pointer_width = "32")] + pub fn stx_mtime(&self) -> Option<&libc::statx_timestamp> { + if let Some(ext) = &self.statx_extra_fields { + if (ext.stx_mask & libc::STATX_MTIME) != 0 { + return Some(&ext.stx_mtime); + } + } + None + } + + #[cfg(target_pointer_width = "32")] + pub fn stx_atime(&self) -> Option<&libc::statx_timestamp> { + if let Some(ext) = &self.statx_extra_fields { + if (ext.stx_mask & libc::STATX_ATIME) != 0 { + return Some(&ext.stx_atime); + } + } + None + } + + #[cfg(target_pointer_width = "32")] + pub fn stx_ctime(&self) -> Option<&libc::statx_timestamp> { + if let Some(ext) = &self.statx_extra_fields { + if (ext.stx_mask & libc::STATX_CTIME) != 0 { + return Some(&ext.stx_ctime); + } + } + None + } } } else { impl FileAttr { @@ -335,24 +381,15 @@ impl FileAttr { #[cfg(target_os = "netbsd")] impl FileAttr { pub fn modified(&self) -> io::Result { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_mtime as libc::time_t, - tv_nsec: self.stat.st_mtimensec as libc::c_long, - })) + Ok(SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtimensec as i64)) } pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_atime as libc::time_t, - tv_nsec: self.stat.st_atimensec as libc::c_long, - })) + Ok(SystemTime::new(self.stat.st_atime as i64, self.stat.st_atimensec as i64)) } pub fn created(&self) -> io::Result { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_birthtime as libc::time_t, - tv_nsec: self.stat.st_birthtimensec as libc::c_long, - })) + Ok(SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtimensec as i64)) } } @@ -360,34 +397,36 @@ impl FileAttr { impl FileAttr { #[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))] pub fn modified(&self) -> io::Result { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_mtime as libc::time_t, - tv_nsec: self.stat.st_mtime_nsec as _, - })) + #[cfg(target_pointer_width = "32")] + cfg_has_statx! { + if let Some(mtime) = self.stx_mtime() { + return Ok(SystemTime::new(mtime.tv_sec, mtime.tv_nsec as i64)); + } + } + + Ok(SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtime_nsec as i64)) } #[cfg(any(target_os = "vxworks", target_os = "espidf"))] pub fn modified(&self) -> io::Result { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_mtime as libc::time_t, - tv_nsec: 0, - })) + Ok(SystemTime::new(self.stat.st_mtime as i64, 0)) } #[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))] pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_atime as libc::time_t, - tv_nsec: self.stat.st_atime_nsec as _, - })) + #[cfg(target_pointer_width = "32")] + cfg_has_statx! { + if let Some(atime) = self.stx_atime() { + return Ok(SystemTime::new(atime.tv_sec, atime.tv_nsec as i64)); + } + } + + Ok(SystemTime::new(self.stat.st_atime as i64, self.stat.st_atime_nsec as i64)) } #[cfg(any(target_os = "vxworks", target_os = "espidf"))] pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_atime as libc::time_t, - tv_nsec: 0, - })) + Ok(SystemTime::new(self.stat.st_atime as i64, 0)) } #[cfg(any( @@ -397,10 +436,7 @@ impl FileAttr { target_os = "ios" ))] pub fn created(&self) -> io::Result { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_birthtime as libc::time_t, - tv_nsec: self.stat.st_birthtime_nsec as libc::c_long, - })) + Ok(SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtime_nsec as i64)) } #[cfg(not(any( @@ -413,10 +449,7 @@ impl FileAttr { cfg_has_statx! { if let Some(ext) = &self.statx_extra_fields { return if (ext.stx_mask & libc::STATX_BTIME) != 0 { - Ok(SystemTime::from(libc::timespec { - tv_sec: ext.stx_btime.tv_sec as libc::time_t, - tv_nsec: ext.stx_btime.tv_nsec as _, - })) + Ok(SystemTime::new(ext.stx_btime.tv_sec, ext.stx_btime.tv_nsec as i64)) } else { Err(io::const_io_error!( io::ErrorKind::Uncategorized, diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index 20d061206be..5000e7d890b 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -144,6 +144,10 @@ mod inner { } impl SystemTime { + pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { + SystemTime { t: Timespec::new(tv_sec, tv_nsec) } + } + pub fn now() -> SystemTime { use crate::ptr; @@ -294,6 +298,10 @@ mod inner { } impl SystemTime { + pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { + SystemTime { t: Timespec::new(tv_sec, tv_nsec) } + } + pub fn now() -> SystemTime { SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) } } From f9675185a32564a8b0c455ec1da16c9a62e74b4d Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 6 May 2022 11:45:59 -0700 Subject: [PATCH 4/4] Share more unix SystemTime code --- library/std/src/sys/unix/time.rs | 158 +++++++++++-------------------- 1 file changed, 57 insertions(+), 101 deletions(-) diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index 5000e7d890b..ac8355188bb 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -1,9 +1,16 @@ +use crate::fmt; use crate::time::Duration; -pub use self::inner::{Instant, SystemTime, UNIX_EPOCH}; +pub use self::inner::Instant; use crate::convert::TryInto; const NSEC_PER_SEC: u64 = 1_000_000_000; +pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SystemTime { + pub(in crate::sys::unix) t: Timespec, +} #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub(in crate::sys::unix) struct Timespec { @@ -11,6 +18,39 @@ pub(in crate::sys::unix) struct Timespec { tv_nsec: i64, } +impl SystemTime { + pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { + SystemTime { t: Timespec::new(tv_sec, tv_nsec) } + } + + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.t.sub_timespec(&other.t) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(SystemTime { t: self.t.checked_add_duration(other)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(SystemTime { t: self.t.checked_sub_duration(other)? }) + } +} + +impl From for SystemTime { + fn from(t: libc::timespec) -> SystemTime { + SystemTime { t: Timespec::from(t) } + } +} + +impl fmt::Debug for SystemTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SystemTime") + .field("tv_sec", &self.t.tv_sec) + .field("tv_nsec", &self.t.tv_nsec) + .finish() + } +} + impl Timespec { const fn zero() -> Timespec { Timespec { tv_sec: 0, tv_nsec: 0 } @@ -85,31 +125,36 @@ impl Timespec { } Some(Timespec::new(secs, nsec as i64)) } + + pub fn to_timespec(&self) -> Option { + use crate::convert::TryInto; + Some(libc::timespec { + tv_sec: self.tv_sec.try_into().ok()?, + tv_nsec: self.tv_nsec.try_into().ok()?, + }) + } +} + +impl From for Timespec { + fn from(t: libc::timespec) -> Timespec { + Timespec::new(t.tv_sec as i64, t.tv_nsec as i64) + } } #[cfg(any(target_os = "macos", target_os = "ios"))] mod inner { - use crate::fmt; use crate::sync::atomic::{AtomicU64, Ordering}; use crate::sys::cvt; use crate::sys_common::mul_div_u64; use crate::time::Duration; - use super::Timespec; - use super::NSEC_PER_SEC; + use super::{SystemTime, Timespec, NSEC_PER_SEC}; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct Instant { t: u64, } - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct SystemTime { - pub(in crate::sys::unix) t: Timespec, - } - - pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; - #[repr(C)] #[derive(Copy, Clone)] struct mach_timebase_info { @@ -144,10 +189,6 @@ mod inner { } impl SystemTime { - pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { - SystemTime { t: Timespec::new(tv_sec, tv_nsec) } - } - pub fn now() -> SystemTime { use crate::ptr; @@ -155,18 +196,6 @@ mod inner { cvt(unsafe { libc::gettimeofday(&mut s, ptr::null_mut()) }).unwrap(); return SystemTime::from(s); } - - pub fn sub_time(&self, other: &SystemTime) -> Result { - self.t.sub_timespec(&other.t) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime { t: self.t.checked_add_duration(other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime { t: self.t.checked_sub_duration(other)? }) - } } impl From for Timespec { @@ -181,27 +210,6 @@ mod inner { } } - impl From for Timespec { - fn from(t: libc::timespec) -> Timespec { - Timespec::new(t.tv_sec as i64, t.tv_nsec as i64) - } - } - - impl From for SystemTime { - fn from(t: libc::timespec) -> SystemTime { - SystemTime { t: Timespec::from(t) } - } - } - - impl fmt::Debug for SystemTime { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SystemTime") - .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec) - .finish() - } - } - fn checked_dur2intervals(dur: &Duration) -> Option { let nanos = dur.as_secs().checked_mul(NSEC_PER_SEC)?.checked_add(dur.subsec_nanos() as u64)?; @@ -256,20 +264,13 @@ mod inner { use crate::sys::cvt; use crate::time::Duration; - use super::Timespec; + use super::{SystemTime, Timespec}; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Instant { t: Timespec, } - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct SystemTime { - pub(in crate::sys::unix) t: Timespec, - } - - pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; - impl Instant { pub fn now() -> Instant { Instant { t: Timespec::now(libc::CLOCK_MONOTONIC) } @@ -298,46 +299,9 @@ mod inner { } impl SystemTime { - pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { - SystemTime { t: Timespec::new(tv_sec, tv_nsec) } - } - pub fn now() -> SystemTime { SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) } } - - pub fn sub_time(&self, other: &SystemTime) -> Result { - self.t.sub_timespec(&other.t) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime { t: self.t.checked_add_duration(other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime { t: self.t.checked_sub_duration(other)? }) - } - } - - impl From for Timespec { - fn from(t: libc::timespec) -> Timespec { - Timespec::new(t.tv_sec as i64, t.tv_nsec as i64) - } - } - - impl From for SystemTime { - fn from(t: libc::timespec) -> SystemTime { - SystemTime { t: Timespec::from(t) } - } - } - - impl fmt::Debug for SystemTime { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SystemTime") - .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec) - .finish() - } } #[cfg(not(any(target_os = "dragonfly", target_os = "espidf")))] @@ -378,13 +342,5 @@ mod inner { cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap(); Timespec::from(unsafe { t.assume_init() }) } - - pub fn to_timespec(&self) -> Option { - use crate::convert::TryInto; - Some(libc::timespec { - tv_sec: self.tv_sec.try_into().ok()?, - tv_nsec: self.tv_nsec.try_into().ok()?, - }) - } } }