Skip to content

Commit

Permalink
time: use clock_gettime on macos
Browse files Browse the repository at this point in the history
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
time-rs/time@cc367ed
which predated the introduction of `clock_gettime` support in macOS
10.12 Sierra which became the minimum supported version in
58bbca9.

Note that this change was made for aarch64 in
5008a31 which predated 10.12 becoming
the minimum supported version. The discussion took place in
rust-lang#91417 and in particular
rust-lang#91417 (comment)
and
rust-lang#91417 (comment)
are relevant.
  • Loading branch information
tamird committed Sep 28, 2023
1 parent b21eb4f commit bc30010
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 150 deletions.
192 changes: 44 additions & 148 deletions library/std/src/sys/unix/time.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use crate::fmt;
use crate::time::Duration;

pub use self::inner::Instant;

const NSEC_PER_SEC: u64 = 1_000_000_000;
pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
#[allow(dead_code)] // Used for pthread condvar timeouts
Expand Down Expand Up @@ -250,161 +248,59 @@ impl From<__timespec64> for Timespec {
}
}

#[cfg(any(
all(target_os = "macos", not(target_arch = "aarch64")),
target_os = "ios",
target_os = "watchos",
target_os = "tvos"
))]
mod inner {
use crate::sync::atomic::{AtomicU64, Ordering};
use crate::sys_common::mul_div_u64;
use crate::time::Duration;

use super::{SystemTime, Timespec, NSEC_PER_SEC};

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
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 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))
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instant {
t: Timespec,
}

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.
impl Instant {
pub fn now() -> Instant {
// https://www.manpagez.com/man/3/clock_gettime/
//
// Encoding this as a single `AtomicU64` allows us to use `Relaxed`
// operations, as we are only interested in the effects on a single
// memory location.
static INFO_BITS: AtomicU64 = AtomicU64::new(0);

// If a previous thread has initialized `INFO_BITS`, use it.
let info_bits = INFO_BITS.load(Ordering::Relaxed);
if info_bits != 0 {
return info_from_bits(info_bits);
}

// ... otherwise learn for ourselves ...
extern "C" {
fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t;
}

let mut info = info_from_bits(0);
unsafe {
mach_timebase_info(&mut info);
}
INFO_BITS.store(info_to_bits(info), Ordering::Relaxed);
info
// CLOCK_UPTIME_RAW clock that increments monotonically, in the same man-
// ner as CLOCK_MONOTONIC_RAW, but that does not incre-
// ment while the system is asleep. The returned value
// is identical to the result of mach_absolute_time()
// after the appropriate mach_timebase conversion is
// applied.
//
// Instant on macos was historically implemented using mach_absolute_time;
// we preserve this value domain out of an abundance of caution.
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "watchos",
target_os = "tvos"
))]
const clock_id: libc::clockid_t = libc::CLOCK_UPTIME_RAW;
#[cfg(not(any(
target_os = "macos",
target_os = "ios",
target_os = "watchos",
target_os = "tvos"
)))]
const clock_id: libc::clockid_t = libc::CLOCK_MONOTONIC;
Instant { t: Timespec::now(clock_id) }
}

#[inline]
fn info_to_bits(info: mach_timebase_info) -> u64 {
((info.denom as u64) << 32) | (info.numer as u64)
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.t.sub_timespec(&other.t).ok()
}

#[inline]
fn info_from_bits(bits: u64) -> mach_timebase_info {
mach_timebase_info { numer: bits as u32, denom: (bits >> 32) as u32 }
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_add_duration(other)? })
}
}

#[cfg(not(any(
all(target_os = "macos", not(target_arch = "aarch64")),
target_os = "ios",
target_os = "watchos",
target_os = "tvos"
)))]
mod inner {
use crate::fmt;
use crate::time::Duration;

use super::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)? })
}
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 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()
}
}
3 changes: 1 addition & 2 deletions library/std/src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub use core::time::TryFromFloatSecsError;
/// |-----------|----------------------------------------------------------------------|
/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] |
/// | UNIX | [clock_gettime (Monotonic Clock)] |
/// | Darwin | [mach_absolute_time] |
/// | Darwin | [clock_gettime (Monotonic Clock)] |
/// | VXWorks | [clock_gettime (Monotonic Clock)] |
/// | SOLID | `get_tim` |
/// | WASI | [__wasi_clock_time_get (Monotonic Clock)] |
Expand All @@ -121,7 +121,6 @@ pub use core::time::TryFromFloatSecsError;
/// [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
/// [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.
///
Expand Down

0 comments on commit bc30010

Please sign in to comment.