Auto merge of #96657 - cuviper:time64, r=joshtriplett
Use 64-bit time on 32-bit linux-gnu The standard library suffered the [Year 2038 problem][Y2038] in two main places on targets with 32-bit `time_t`: - In `std::time::SystemTime`, we stored a `timespec` that has `time_t` seconds. This is now changed to directly store 64-bit seconds and nanoseconds, and on 32-bit linux-gnu we try to use `__clock_gettime64` (glibc 2.34+) to get the larger timestamp. - In `std::fs::Metadata`, we store a `stat64`, which has 64-bit `off_t` but still 32-bit `time_t`, and unfortunately that is baked in the API by the (deprecated) `MetadataExt::as_raw_stat()`. However, we can use `statx` for 64-bit `statx_timestamp` to store in addition to the `stat64`, as we already do to support creation time, and the rest of the `MetadataExt` methods can return those full values. Note that some filesystems may still be limited in their actual timestamp support, but that's not something Rust can change. There remain a few places that need `timespec` for system call timeouts -- I leave that to future work. [Y2038]: https://en.wikipedia.org/wiki/Year_2038_problem
This commit is contained in:
commit
24a0eecf03
|
@ -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
|
||||
|
|
|
@ -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<SystemTime> {
|
||||
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<SystemTime> {
|
||||
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<SystemTime> {
|
||||
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<SystemTime> {
|
||||
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<SystemTime> {
|
||||
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<SystemTime> {
|
||||
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<SystemTime> {
|
||||
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<SystemTime> {
|
||||
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,
|
||||
|
|
|
@ -24,8 +24,9 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -
|
|||
// 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<Duration>) -
|
|||
// 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<Duration>) -
|
|||
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::<u32>(), // This argument is unused for FUTEX_WAIT_BITSET.
|
||||
!0u32, // A full bitmask, to make it behave like a regular FUTEX_WAIT.
|
||||
)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1,21 +1,63 @@
|
|||
use crate::cmp::Ordering;
|
||||
use crate::fmt;
|
||||
use crate::time::Duration;
|
||||
|
||||
use core::hash::{Hash, Hasher};
|
||||
|
||||
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)]
|
||||
#[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 {
|
||||
pub t: libc::timespec,
|
||||
tv_sec: i64,
|
||||
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<Duration, Duration> {
|
||||
self.t.sub_timespec(&other.t)
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
Some(SystemTime { t: self.t.checked_add_duration(other)? })
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
Some(SystemTime { t: self.t.checked_sub_duration(other)? })
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libc::timespec> 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 { 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<Duration, Duration> {
|
||||
|
@ -23,22 +65,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,89 +96,65 @@ impl Timespec {
|
|||
pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
|
||||
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<Timespec> {
|
||||
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 _ } })
|
||||
Some(Timespec::new(secs, nsec as i64))
|
||||
}
|
||||
|
||||
pub fn to_timespec(&self) -> Option<libc::timespec> {
|
||||
use crate::convert::TryInto;
|
||||
Some(libc::timespec {
|
||||
tv_sec: self.tv_sec.try_into().ok()?,
|
||||
tv_nsec: self.tv_nsec.try_into().ok()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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<Ordering> {
|
||||
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<H: Hasher>(&self, state: &mut H) {
|
||||
self.t.tv_sec.hash(state);
|
||||
self.t.tv_nsec.hash(state);
|
||||
impl From<libc::timespec> 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 {
|
||||
|
@ -178,41 +196,17 @@ mod inner {
|
|||
cvt(unsafe { libc::gettimeofday(&mut s, ptr::null_mut()) }).unwrap();
|
||||
return SystemTime::from(s);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
|
||||
self.t.sub_timespec(&other.t)
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
Some(SystemTime { t: self.t.checked_add_duration(other)? })
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
Some(SystemTime { t: self.t.checked_sub_duration(other)? })
|
||||
impl From<libc::timeval> for Timespec {
|
||||
fn from(t: libc::timeval) -> Timespec {
|
||||
Timespec::new(t.tv_sec as i64, 1000 * t.tv_usec as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libc::timeval> 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libc::timespec> for SystemTime {
|
||||
fn from(t: libc::timespec) -> SystemTime {
|
||||
SystemTime { t: Timespec { 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)
|
||||
.finish()
|
||||
SystemTime { t: Timespec::from(t) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,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) }
|
||||
|
@ -305,8 +292,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()
|
||||
}
|
||||
}
|
||||
|
@ -315,33 +302,6 @@ mod inner {
|
|||
pub fn now() -> SystemTime {
|
||||
SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) }
|
||||
}
|
||||
|
||||
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
|
||||
self.t.sub_timespec(&other.t)
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
Some(SystemTime { t: self.t.checked_add_duration(other)? })
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
Some(SystemTime { t: self.t.checked_sub_duration(other)? })
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libc::timespec> for SystemTime {
|
||||
fn from(t: libc::timespec) -> SystemTime {
|
||||
SystemTime { t: Timespec { 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)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "dragonfly", target_os = "espidf")))]
|
||||
|
@ -351,9 +311,36 @@ 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 { t: unsafe { t.assume_init() } }
|
||||
Timespec::from(unsafe { t.assume_init() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue