1
Fork 0

Auto merge of #116238 - tamird:gettimeofday, r=thomcc

time: use clock_gettime on macos

Replace `gettimeofday` with `clock_gettime(CLOCK_REALTIME)` on:

```
all(target_os = "macos", not(target_arch = "aarch64")),
    target_os = "ios",
    target_os = "watchos",
    target_os = "tvos"
))]
```

`gettimeofday` was first used in
cc367edd95
which predated the introduction of `clock_gettime` support in macOS
10.12 Sierra which became the minimum supported version in
58bbca958d.

Replace `mach_{absolute_time,timebase_info}` with
`clock_gettime(CLOCK_REALTIME)` on:

```
all(target_os = "macos", not(target_arch = "aarch64")),
    target_os = "ios",
    target_os = "watchos",
    target_os = "tvos"
))]
```

`mach_{absolute_time,timebase_info}` were first used in
cc367edd95
which predated the introduction of `clock_gettime` support in macOS
10.12 Sierra which became the minimum supported version in
58bbca958d.

Note that this change was made for aarch64 in
5008a317ce which predated 10.12 becoming
the minimum supported version. The discussion took place in
https://github.com/rust-lang/rust/issues/91417 and in particular
https://github.com/rust-lang/rust/issues/91417#issuecomment-992151582
and
https://github.com/rust-lang/rust/issues/91417#issuecomment-1033048064
are relevant.
This commit is contained in:
bors 2023-10-24 04:15:39 +00:00
commit e918db897d
2 changed files with 81 additions and 201 deletions

View file

@ -1,8 +1,6 @@
use crate::fmt; use crate::fmt;
use crate::time::Duration; use crate::time::Duration;
pub use self::inner::Instant;
const NSEC_PER_SEC: u64 = 1_000_000_000; const NSEC_PER_SEC: u64 = 1_000_000_000;
pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
#[allow(dead_code)] // Used for pthread condvar timeouts #[allow(dead_code)] // Used for pthread condvar timeouts
@ -40,6 +38,10 @@ impl SystemTime {
SystemTime { t: Timespec::new(tv_sec, tv_nsec) } 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<Duration, Duration> { pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
self.t.sub_timespec(&other.t) self.t.sub_timespec(&other.t)
} }
@ -79,6 +81,36 @@ impl Timespec {
Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } } Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } }
} }
pub fn now(clock: libc::clockid_t) -> Timespec {
use crate::mem::MaybeUninit;
use crate::sys::cvt;
// Try to use 64-bit time in preparation for Y2038.
#[cfg(all(
target_os = "linux",
target_env = "gnu",
target_pointer_width = "32",
not(target_arch = "riscv32")
))]
{
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);
if let Some(clock_gettime64) = __clock_gettime64.get() {
let mut t = MaybeUninit::uninit();
cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap();
return Timespec::from(unsafe { t.assume_init() });
}
}
let mut t = MaybeUninit::uninit();
cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap();
Timespec::from(unsafe { t.assume_init() })
}
pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
if self >= other { if self >= other {
// NOTE(eddyb) two aspects of this `if`-`else` are required for LLVM // NOTE(eddyb) two aspects of this `if`-`else` are required for LLVM
@ -216,209 +248,59 @@ impl From<__timespec64> for Timespec {
} }
} }
#[cfg(any( #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
all(target_os = "macos", any(not(target_arch = "aarch64"))), pub struct Instant {
target_os = "ios", t: Timespec,
target_os = "watchos", }
target_os = "tvos"
))]
mod inner {
use crate::sync::atomic::{AtomicU64, Ordering};
use crate::sys::cvt;
use crate::sys_common::mul_div_u64;
use crate::time::Duration;
use super::{SystemTime, Timespec, NSEC_PER_SEC}; impl Instant {
pub fn now() -> Instant {
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] // https://www.manpagez.com/man/3/clock_gettime/
pub struct Instant {
t: u64,
}
#[repr(C)]
#[derive(Copy, Clone)]
struct mach_timebase_info {
numer: u32,
denom: u32,
}
type mach_timebase_info_t = *mut mach_timebase_info;
type kern_return_t = libc::c_int;
impl Instant {
pub fn now() -> Instant {
extern "C" {
fn mach_absolute_time() -> u64;
}
Instant { t: unsafe { mach_absolute_time() } }
}
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
let diff = self.t.checked_sub(other.t)?;
let info = info();
let nanos = mul_div_u64(diff, info.numer as u64, info.denom as u64);
Some(Duration::new(nanos / NSEC_PER_SEC, (nanos % NSEC_PER_SEC) as u32))
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_add(checked_dur2intervals(other)?)? })
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_sub(checked_dur2intervals(other)?)? })
}
}
impl SystemTime {
pub fn now() -> SystemTime {
use crate::ptr;
let mut s = libc::timeval { tv_sec: 0, tv_usec: 0 };
cvt(unsafe { libc::gettimeofday(&mut s, ptr::null_mut()) }).unwrap();
return SystemTime::from(s);
}
}
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 { t: Timespec::from(t) }
}
}
fn checked_dur2intervals(dur: &Duration) -> Option<u64> {
let nanos =
dur.as_secs().checked_mul(NSEC_PER_SEC)?.checked_add(dur.subsec_nanos() as u64)?;
let info = info();
Some(mul_div_u64(nanos, info.denom as u64, info.numer as u64))
}
fn info() -> mach_timebase_info {
// INFO_BITS conceptually is an `Option<mach_timebase_info>`. We can do
// this in 64 bits because we know 0 is never a valid value for the
// `denom` field.
// //
// Encoding this as a single `AtomicU64` allows us to use `Relaxed` // CLOCK_UPTIME_RAW clock that increments monotonically, in the same man-
// operations, as we are only interested in the effects on a single // ner as CLOCK_MONOTONIC_RAW, but that does not incre-
// memory location. // ment while the system is asleep. The returned value
static INFO_BITS: AtomicU64 = AtomicU64::new(0); // is identical to the result of mach_absolute_time()
// after the appropriate mach_timebase conversion is
// If a previous thread has initialized `INFO_BITS`, use it. // applied.
let info_bits = INFO_BITS.load(Ordering::Relaxed); //
if info_bits != 0 { // Instant on macos was historically implemented using mach_absolute_time;
return info_from_bits(info_bits); // we preserve this value domain out of an abundance of caution.
} #[cfg(any(
target_os = "macos",
// ... otherwise learn for ourselves ... target_os = "ios",
extern "C" { target_os = "watchos",
fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t; target_os = "tvos"
} ))]
const clock_id: libc::clockid_t = libc::CLOCK_UPTIME_RAW;
let mut info = info_from_bits(0); #[cfg(not(any(
unsafe { target_os = "macos",
mach_timebase_info(&mut info); target_os = "ios",
} target_os = "watchos",
INFO_BITS.store(info_to_bits(info), Ordering::Relaxed); target_os = "tvos"
info )))]
const clock_id: libc::clockid_t = libc::CLOCK_MONOTONIC;
Instant { t: Timespec::now(clock_id) }
} }
#[inline] pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
fn info_to_bits(info: mach_timebase_info) -> u64 { self.t.sub_timespec(&other.t).ok()
((info.denom as u64) << 32) | (info.numer as u64)
} }
#[inline] pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
fn info_from_bits(bits: u64) -> mach_timebase_info { Some(Instant { t: self.t.checked_add_duration(other)? })
mach_timebase_info { numer: bits as u32, denom: (bits >> 32) as u32 } }
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_sub_duration(other)? })
} }
} }
#[cfg(not(any( impl fmt::Debug for Instant {
all(target_os = "macos", any(not(target_arch = "aarch64"))), fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
target_os = "ios", f.debug_struct("Instant")
target_os = "watchos", .field("tv_sec", &self.t.tv_sec)
target_os = "tvos" .field("tv_nsec", &self.t.tv_nsec.0)
)))] .finish()
mod inner {
use crate::fmt;
use crate::mem::MaybeUninit;
use crate::sys::cvt;
use crate::time::Duration;
use super::{SystemTime, Timespec};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instant {
t: Timespec,
}
impl Instant {
pub fn now() -> Instant {
#[cfg(target_os = "macos")]
const clock_id: libc::clockid_t = libc::CLOCK_UPTIME_RAW;
#[cfg(not(target_os = "macos"))]
const clock_id: libc::clockid_t = libc::CLOCK_MONOTONIC;
Instant { t: Timespec::now(clock_id) }
}
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.t.sub_timespec(&other.t).ok()
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_add_duration(other)? })
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_sub_duration(other)? })
}
}
impl fmt::Debug for Instant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Instant")
.field("tv_sec", &self.t.tv_sec)
.field("tv_nsec", &self.t.tv_nsec.0)
.finish()
}
}
impl SystemTime {
pub fn now() -> SystemTime {
SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) }
}
}
impl Timespec {
pub fn now(clock: libc::clockid_t) -> Timespec {
// Try to use 64-bit time in preparation for Y2038.
#[cfg(all(
target_os = "linux",
target_env = "gnu",
target_pointer_width = "32",
not(target_arch = "riscv32")
))]
{
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 super::__timespec64) -> libc::c_int);
if let Some(clock_gettime64) = __clock_gettime64.get() {
let mut t = MaybeUninit::uninit();
cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap();
return Timespec::from(unsafe { t.assume_init() });
}
}
let mut t = MaybeUninit::uninit();
cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap();
Timespec::from(unsafe { t.assume_init() })
}
} }
} }

View file

@ -111,7 +111,7 @@ pub use core::time::TryFromFloatSecsError;
/// |-----------|----------------------------------------------------------------------| /// |-----------|----------------------------------------------------------------------|
/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] | /// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] |
/// | UNIX | [clock_gettime (Monotonic Clock)] | /// | UNIX | [clock_gettime (Monotonic Clock)] |
/// | Darwin | [mach_absolute_time] | /// | Darwin | [clock_gettime (Monotonic Clock)] |
/// | VXWorks | [clock_gettime (Monotonic Clock)] | /// | VXWorks | [clock_gettime (Monotonic Clock)] |
/// | SOLID | `get_tim` | /// | SOLID | `get_tim` |
/// | WASI | [__wasi_clock_time_get (Monotonic Clock)] | /// | WASI | [__wasi_clock_time_get (Monotonic Clock)] |
@ -123,7 +123,6 @@ pub use core::time::TryFromFloatSecsError;
/// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode /// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode
/// [__wasi_clock_time_get (Monotonic Clock)]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get /// [__wasi_clock_time_get (Monotonic Clock)]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get
/// [clock_gettime (Monotonic Clock)]: https://linux.die.net/man/3/clock_gettime /// [clock_gettime (Monotonic Clock)]: https://linux.die.net/man/3/clock_gettime
/// [mach_absolute_time]: https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/KernelProgramming/services/services.html
/// ///
/// **Disclaimer:** These system calls might change over time. /// **Disclaimer:** These system calls might change over time.
/// ///
@ -224,7 +223,7 @@ pub struct Instant(time::Instant);
/// |-----------|----------------------------------------------------------------------| /// |-----------|----------------------------------------------------------------------|
/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] | /// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] |
/// | UNIX | [clock_gettime (Realtime Clock)] | /// | UNIX | [clock_gettime (Realtime Clock)] |
/// | Darwin | [gettimeofday] | /// | Darwin | [clock_gettime (Realtime Clock)] |
/// | VXWorks | [clock_gettime (Realtime Clock)] | /// | VXWorks | [clock_gettime (Realtime Clock)] |
/// | SOLID | `SOLID_RTC_ReadTime` | /// | SOLID | `SOLID_RTC_ReadTime` |
/// | WASI | [__wasi_clock_time_get (Realtime Clock)] | /// | WASI | [__wasi_clock_time_get (Realtime Clock)] |
@ -233,7 +232,6 @@ pub struct Instant(time::Instant);
/// [currently]: crate::io#platform-specific-behavior /// [currently]: crate::io#platform-specific-behavior
/// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time /// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time
/// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode /// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode
/// [gettimeofday]: https://man7.org/linux/man-pages/man2/gettimeofday.2.html
/// [clock_gettime (Realtime Clock)]: https://linux.die.net/man/3/clock_gettime /// [clock_gettime (Realtime Clock)]: https://linux.die.net/man/3/clock_gettime
/// [__wasi_clock_time_get (Realtime Clock)]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get /// [__wasi_clock_time_get (Realtime Clock)]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get
/// [GetSystemTimePreciseAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime /// [GetSystemTimePreciseAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime