Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

time: use clock_gettime on macos #116238

Merged
merged 3 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
274 changes: 78 additions & 196 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 @@ -40,6 +38,10 @@ impl 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<Duration, Duration> {
self.t.sub_timespec(&other.t)
}
Expand Down Expand Up @@ -79,6 +81,36 @@ impl Timespec {
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> {
if self >= other {
// NOTE(eddyb) two aspects of this `if`-`else` are required for LLVM
Expand Down Expand Up @@ -216,209 +248,59 @@ impl From<__timespec64> for Timespec {
}
}

#[cfg(any(
all(target_os = "macos", any(not(target_arch = "aarch64"))),
target_os = "ios",
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};

#[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 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`
// 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
}

#[inline]
fn info_to_bits(info: mach_timebase_info) -> u64 {
((info.denom as u64) << 32) | (info.numer as u64)
}

#[inline]
fn info_from_bits(bits: u64) -> mach_timebase_info {
mach_timebase_info { numer: bits as u32, denom: (bits >> 32) as u32 }
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instant {
t: Timespec,
}

#[cfg(not(any(
all(target_os = "macos", any(not(target_arch = "aarch64"))),
target_os = "ios",
target_os = "watchos",
target_os = "tvos"
)))]
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 {
// https://www.manpagez.com/man/3/clock_gettime/
//
// 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) }
}

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_instant(&self, other: &Instant) -> Option<Duration> {
self.t.sub_timespec(&other.t).ok()
}

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()
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_add_duration(other)? })
}

impl SystemTime {
pub fn now() -> SystemTime {
SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) }
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_sub_duration(other)? })
}
}

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() })
}
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()
}
}
6 changes: 2 additions & 4 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 Expand Up @@ -221,7 +220,7 @@ pub struct Instant(time::Instant);
/// |-----------|----------------------------------------------------------------------|
/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] |
/// | UNIX | [clock_gettime (Realtime Clock)] |
/// | Darwin | [gettimeofday] |
/// | Darwin | [clock_gettime (Realtime Clock)] |
/// | VXWorks | [clock_gettime (Realtime Clock)] |
/// | SOLID | `SOLID_RTC_ReadTime` |
/// | WASI | [__wasi_clock_time_get (Realtime Clock)] |
Expand All @@ -230,7 +229,6 @@ pub struct Instant(time::Instant);
/// [currently]: crate::io#platform-specific-behavior
/// [`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
/// [gettimeofday]: https://man7.org/linux/man-pages/man2/gettimeofday.2.html
/// [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
/// [GetSystemTimePreciseAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime
Expand Down
Loading