pub use self::inner::Instant;
const NSEC_PER_SEC: u64 = 1_000_000_000;
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "watchos",
target_os = "tvos"
))]
mod inner {
use crate::sys_common::mul_div_u64;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Duration;
use super::NSEC_PER_SEC;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Instant {
t: u64,
}
#[repr(C)]
#[derive(Copy, Clone)]
#[allow(non_camel_case_types)]
struct mach_timebase_info {
numer: u32,
denom: u32,
}
#[allow(non_camel_case_types)]
type mach_timebase_info_t = *mut mach_timebase_info;
#[allow(non_camel_case_types)]
type kern_return_t = libc::c_int;
impl Instant {
pub fn now() -> Instant {
extern "C" {
fn mach_continuous_time() -> u64;
}
Instant {
t: unsafe { mach_continuous_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)?)?,
})
}
}
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 {
static INFO_BITS: AtomicU64 = AtomicU64::new(0);
let info_bits = INFO_BITS.load(Ordering::Relaxed);
if info_bits != 0 {
return info_from_bits(info_bits);
}
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,
}
}
}
#[cfg(not(any(
target_os = "macos",
target_os = "ios",
target_os = "watchos",
target_os = "tvos"
)))]
mod inner {
use super::NSEC_PER_SEC;
use std::fmt;
use std::mem::MaybeUninit;
use std::time::Duration;
#[doc(hidden)]
trait IsMinusOne {
fn is_minus_one(&self) -> bool;
}
macro_rules! impl_is_minus_one {
($($t:ident)*) => ($(impl IsMinusOne for $t {
fn is_minus_one(&self) -> bool {
*self == -1
}
})*)
}
impl_is_minus_one! { i8 i16 i32 i64 isize }
fn cvt<T: IsMinusOne>(t: T) -> std::io::Result<T> {
if t.is_minus_one() {
Err(std::io::Error::last_os_error())
} else {
Ok(t)
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
struct Nanoseconds(u32);
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(in crate::sys::unix) struct Timespec {
tv_sec: i64,
tv_nsec: Nanoseconds,
}
impl Timespec {
const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec {
assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64);
Timespec {
tv_sec,
tv_nsec: Nanoseconds(tv_nsec as u32),
}
}
pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
if self >= other {
let (secs, nsec) = if self.tv_nsec.0 >= other.tv_nsec.0 {
(
(self.tv_sec - other.tv_sec) as u64,
self.tv_nsec.0 - other.tv_nsec.0,
)
} else {
(
(self.tv_sec - other.tv_sec - 1) as u64,
self.tv_nsec.0 + (NSEC_PER_SEC as u32) - other.tv_nsec.0,
)
};
Ok(Duration::new(secs, nsec))
} else {
match other.sub_timespec(self) {
Ok(d) => Err(d),
Err(d) => Ok(d),
}
}
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?;
let mut nsec = other.subsec_nanos() + self.tv_nsec.0;
if nsec >= NSEC_PER_SEC as u32 {
nsec -= NSEC_PER_SEC as u32;
secs = secs.checked_add(1)?;
}
Some(Timespec::new(secs, nsec.into()))
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?;
let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32;
if nsec < 0 {
nsec += NSEC_PER_SEC as i32;
secs = secs.checked_sub(1)?;
}
Some(Timespec::new(secs, nsec.into()))
}
#[allow(dead_code)]
pub fn to_timespec(&self) -> Option<libc::timespec> {
Some(libc::timespec {
tv_sec: self.tv_sec.try_into().ok()?,
tv_nsec: self.tv_nsec.0.try_into().ok()?,
})
}
#[cfg(target_os = "nto")]
pub(super) fn to_timespec_capped(&self) -> Option<libc::timespec> {
if (self.tv_nsec.0 as u64)
.checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?)
.is_none()
{
return None;
}
self.to_timespec()
}
#[cfg(all(
target_os = "linux",
target_env = "gnu",
target_pointer_width = "32",
not(target_arch = "riscv32")
))]
pub fn to_timespec64(&self) -> __timespec64 {
__timespec64::new(self.tv_sec, self.tv_nsec.0 as _)
}
}
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(all(
target_os = "linux",
target_env = "gnu",
target_pointer_width = "32",
not(target_arch = "riscv32")
))]
#[repr(C)]
pub(in crate::sys::unix) struct __timespec64 {
pub(in crate::sys::unix) tv_sec: i64,
#[cfg(target_endian = "big")]
_padding: i32,
pub(in crate::sys::unix) tv_nsec: i32,
#[cfg(target_endian = "little")]
_padding: i32,
}
#[cfg(all(
target_os = "linux",
target_env = "gnu",
target_pointer_width = "32",
not(target_arch = "riscv32")
))]
impl __timespec64 {
pub(in crate::sys::unix) fn new(tv_sec: i64, tv_nsec: i32) -> Self {
Self {
tv_sec,
tv_nsec,
_padding: 0,
}
}
}
#[cfg(all(
target_os = "linux",
target_env = "gnu",
target_pointer_width = "32",
not(target_arch = "riscv32")
))]
impl From<__timespec64> for Timespec {
fn from(t: __timespec64) -> Timespec {
Timespec::new(t.tv_sec, t.tv_nsec.into())
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instant {
t: Timespec,
}
impl Instant {
pub fn now() -> Instant {
cfg_if::cfg_if! {
if #[cfg(any(
target_os = "linux",
target_os = "l4re",
target_os = "android",
target_os = "openbsd",
))] {
#[allow(non_upper_case_globals)]
const clock_id: libc::clockid_t = libc::CLOCK_BOOTTIME;
} else {
#[allow(non_upper_case_globals)]
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 Timespec {
pub fn now(clock: libc::clockid_t) -> Timespec {
let mut t = MaybeUninit::uninit();
cfg_if::cfg_if! {
if #[cfg(all(
target_os = "linux",
target_env = "gnu",
target_pointer_width = "32",
not(target_arch = "riscv32")
))]
{
unsafe extern "C" {
fn __clock_gettime64(clock_id: i32, tp: *mut __timespec64) -> i32;
}
cvt(unsafe { __clock_gettime64(clock, t.as_mut_ptr()) }).unwrap();
} else {
cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap();
}
}
Timespec::from(unsafe { t.assume_init() })
}
}
}