Rollup merge of #151161 - joboet:move-pal-time, r=tgross35
std: move time implementations to `sys` This is probably the most complex step of rust-lang/rust#117276 so far. Unfortunately, quite some of the internal time logic defined in the PAL is also used in other places like the filesystem code, so this isn't just a series of simple moves. I've left all that logic inside the PAL and only moved the actual `SystemTime`/`Instant` implementations. While there are no functional changes, this PR also contains some slight code cleanups on Windows and Hermit, these are explained in the relevant commits. For additional details see the individual commits, I've tried to make the messages as helpful as possible about what's going on.
This commit is contained in:
commit
80102f389d
32 changed files with 674 additions and 723 deletions
|
|
@ -580,7 +580,8 @@ mod uefi_fs {
|
|||
use crate::path::Path;
|
||||
use crate::ptr::NonNull;
|
||||
use crate::sys::pal::helpers::{self, UefiBox};
|
||||
use crate::sys::time::{self, SystemTime};
|
||||
use crate::sys::pal::system_time;
|
||||
use crate::sys::time::SystemTime;
|
||||
|
||||
pub(crate) struct File {
|
||||
protocol: NonNull<file::Protocol>,
|
||||
|
|
@ -879,7 +880,7 @@ mod uefi_fs {
|
|||
/// conversion to SystemTime, we use the current time to get the timezone in such cases.
|
||||
pub(crate) fn uefi_to_systemtime(mut time: r_efi::efi::Time) -> Option<SystemTime> {
|
||||
time.timezone = if time.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
|
||||
time::system_time_internal::now().timezone
|
||||
system_time::now().timezone
|
||||
} else {
|
||||
time.timezone
|
||||
};
|
||||
|
|
@ -888,7 +889,7 @@ mod uefi_fs {
|
|||
|
||||
/// Convert to UEFI Time with the current timezone.
|
||||
pub(crate) fn systemtime_to_uefi(time: SystemTime) -> r_efi::efi::Time {
|
||||
let now = time::system_time_internal::now();
|
||||
let now = system_time::now();
|
||||
time.to_uefi_loose(now.timezone, now.daylight)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1822,7 +1822,7 @@ impl File {
|
|||
_ => {
|
||||
#[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))]
|
||||
{
|
||||
use crate::sys::{time::__timespec64, weak::weak};
|
||||
use crate::sys::pal::{time::__timespec64, weak::weak};
|
||||
|
||||
// Added in glibc 2.34
|
||||
weak!(
|
||||
|
|
@ -2258,7 +2258,7 @@ fn set_times_impl(p: &CStr, times: FileTimes, follow_symlinks: bool) -> io::Resu
|
|||
let flags = if follow_symlinks { 0 } else { libc::AT_SYMLINK_NOFOLLOW };
|
||||
#[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))]
|
||||
{
|
||||
use crate::sys::{time::__timespec64, weak::weak};
|
||||
use crate::sys::pal::{time::__timespec64, weak::weak};
|
||||
|
||||
// Added in glibc 2.34
|
||||
weak!(
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ pub mod stdio;
|
|||
pub mod sync;
|
||||
pub mod thread;
|
||||
pub mod thread_local;
|
||||
pub mod time;
|
||||
|
||||
// FIXME(117276): remove this, move feature implementations into individual
|
||||
// submodules.
|
||||
|
|
|
|||
|
|
@ -9,10 +9,9 @@ use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut};
|
|||
use crate::net::{Shutdown, SocketAddr};
|
||||
use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd};
|
||||
use crate::sys::fd::FileDesc;
|
||||
use crate::sys::time::Instant;
|
||||
use crate::sys::{AsInner, FromInner, IntoInner};
|
||||
pub use crate::sys::{cvt, cvt_r};
|
||||
use crate::time::Duration;
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::{cmp, mem};
|
||||
|
||||
#[expect(non_camel_case_types)]
|
||||
|
|
|
|||
|
|
@ -1,35 +1,32 @@
|
|||
#![allow(dead_code)]
|
||||
use hermit_abi::{self, timespec};
|
||||
|
||||
use core::hash::{Hash, Hasher};
|
||||
|
||||
use super::hermit_abi::{self, CLOCK_MONOTONIC, CLOCK_REALTIME, timespec};
|
||||
use crate::cmp::Ordering;
|
||||
use crate::ops::{Add, AddAssign, Sub, SubAssign};
|
||||
use crate::hash::{Hash, Hasher};
|
||||
use crate::time::Duration;
|
||||
|
||||
const NSEC_PER_SEC: i32 = 1_000_000_000;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct Timespec {
|
||||
t: timespec,
|
||||
pub struct Timespec {
|
||||
pub t: timespec,
|
||||
}
|
||||
|
||||
impl Timespec {
|
||||
const MAX: Timespec = Self::new(i64::MAX, 1_000_000_000 - 1);
|
||||
pub const MAX: Timespec = Self::new(i64::MAX, 1_000_000_000 - 1);
|
||||
|
||||
const MIN: Timespec = Self::new(i64::MIN, 0);
|
||||
pub const MIN: Timespec = Self::new(i64::MIN, 0);
|
||||
|
||||
const fn zero() -> Timespec {
|
||||
pub const fn zero() -> Timespec {
|
||||
Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } }
|
||||
}
|
||||
|
||||
const fn new(tv_sec: i64, tv_nsec: i32) -> Timespec {
|
||||
pub const fn new(tv_sec: i64, tv_nsec: i32) -> Timespec {
|
||||
assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC);
|
||||
// SAFETY: The assert above checks tv_nsec is within the valid range
|
||||
Timespec { t: timespec { tv_sec, tv_nsec } }
|
||||
}
|
||||
|
||||
fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
|
||||
pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
|
||||
fn sub_ge_to_unsigned(a: i64, b: i64) -> u64 {
|
||||
debug_assert!(a >= b);
|
||||
a.wrapping_sub(b).cast_unsigned()
|
||||
|
|
@ -57,7 +54,7 @@ impl Timespec {
|
|||
}
|
||||
}
|
||||
|
||||
fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
|
||||
let mut secs = self.t.tv_sec.checked_add_unsigned(other.as_secs())?;
|
||||
|
||||
// Nano calculations can't overflow because nanos are <1B which fit
|
||||
|
|
@ -70,7 +67,7 @@ impl Timespec {
|
|||
Some(Timespec { t: timespec { tv_sec: secs, tv_nsec: nsec as _ } })
|
||||
}
|
||||
|
||||
fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
|
||||
let mut secs = self.t.tv_sec.checked_sub_unsigned(other.as_secs())?;
|
||||
|
||||
// Similar to above, nanos can't overflow.
|
||||
|
|
@ -111,132 +108,3 @@ impl Hash for Timespec {
|
|||
self.t.tv_nsec.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||
pub struct Instant(Timespec);
|
||||
|
||||
impl Instant {
|
||||
pub fn now() -> Instant {
|
||||
let mut time: Timespec = Timespec::zero();
|
||||
let _ = unsafe { hermit_abi::clock_gettime(CLOCK_MONOTONIC, &raw mut time.t) };
|
||||
|
||||
Instant(time)
|
||||
}
|
||||
|
||||
#[stable(feature = "time2", since = "1.8.0")]
|
||||
pub fn elapsed(&self) -> Duration {
|
||||
Instant::now() - *self
|
||||
}
|
||||
|
||||
pub fn duration_since(&self, earlier: Instant) -> Duration {
|
||||
self.checked_duration_since(earlier).unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
|
||||
self.checked_sub_instant(&earlier)
|
||||
}
|
||||
|
||||
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
|
||||
self.0.sub_timespec(&other.0).ok()
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
|
||||
Some(Instant(self.0.checked_add_duration(other)?))
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
|
||||
Some(Instant(self.0.checked_sub_duration(other)?))
|
||||
}
|
||||
|
||||
pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
|
||||
self.0.checked_add_duration(&duration).map(Instant)
|
||||
}
|
||||
|
||||
pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
|
||||
self.0.checked_sub_duration(&duration).map(Instant)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Duration> for Instant {
|
||||
type Output = Instant;
|
||||
|
||||
/// # Panics
|
||||
///
|
||||
/// This function may panic if the resulting point in time cannot be represented by the
|
||||
/// underlying data structure. See [`Instant::checked_add`] for a version without panic.
|
||||
fn add(self, other: Duration) -> Instant {
|
||||
self.checked_add(other).expect("overflow when adding duration to instant")
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<Duration> for Instant {
|
||||
fn add_assign(&mut self, other: Duration) {
|
||||
*self = *self + other;
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<Duration> for Instant {
|
||||
type Output = Instant;
|
||||
|
||||
fn sub(self, other: Duration) -> Instant {
|
||||
self.checked_sub(other).expect("overflow when subtracting duration from instant")
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<Duration> for Instant {
|
||||
fn sub_assign(&mut self, other: Duration) {
|
||||
*self = *self - other;
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<Instant> for Instant {
|
||||
type Output = Duration;
|
||||
|
||||
/// Returns the amount of time elapsed from another instant to this one,
|
||||
/// or zero duration if that instant is later than this one.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Previous Rust versions panicked when `other` was later than `self`. Currently this
|
||||
/// method saturates. Future versions may reintroduce the panic in some circumstances.
|
||||
/// See [Monotonicity].
|
||||
///
|
||||
/// [Monotonicity]: Instant#monotonicity
|
||||
fn sub(self, other: Instant) -> Duration {
|
||||
self.duration_since(other)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub struct SystemTime(Timespec);
|
||||
|
||||
pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero());
|
||||
|
||||
impl SystemTime {
|
||||
pub const MAX: SystemTime = SystemTime(Timespec::MAX);
|
||||
|
||||
pub const MIN: SystemTime = SystemTime(Timespec::MIN);
|
||||
|
||||
pub fn new(tv_sec: i64, tv_nsec: i32) -> SystemTime {
|
||||
SystemTime(Timespec::new(tv_sec, tv_nsec))
|
||||
}
|
||||
|
||||
pub fn now() -> SystemTime {
|
||||
let mut time: Timespec = Timespec::zero();
|
||||
let _ = unsafe { hermit_abi::clock_gettime(CLOCK_REALTIME, &raw mut time.t) };
|
||||
|
||||
SystemTime(time)
|
||||
}
|
||||
|
||||
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
|
||||
self.0.sub_timespec(&other.0)
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
Some(SystemTime(self.0.checked_add_duration(other)?))
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
Some(SystemTime(self.0.checked_sub_duration(other)?))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,38 +3,16 @@ use super::error::expect_success;
|
|||
use crate::mem::MaybeUninit;
|
||||
use crate::time::Duration;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||
pub struct Instant(abi::SYSTIM);
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
impl Instant {
|
||||
pub fn now() -> Instant {
|
||||
// Safety: The provided pointer is valid
|
||||
unsafe {
|
||||
let mut out = MaybeUninit::uninit();
|
||||
expect_success(abi::get_tim(out.as_mut_ptr()), &"get_tim");
|
||||
Instant(out.assume_init())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
|
||||
self.0.checked_sub(other.0).map(|ticks| {
|
||||
// `SYSTIM` is measured in microseconds
|
||||
Duration::from_micros(ticks)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
|
||||
// `SYSTIM` is measured in microseconds
|
||||
let ticks = other.as_micros();
|
||||
|
||||
Some(Instant(self.0.checked_add(ticks.try_into().ok()?)?))
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
|
||||
// `SYSTIM` is measured in microseconds
|
||||
let ticks = other.as_micros();
|
||||
|
||||
Some(Instant(self.0.checked_sub(ticks.try_into().ok()?)?))
|
||||
#[inline]
|
||||
pub fn get_tim() -> abi::SYSTIM {
|
||||
// Safety: The provided pointer is valid
|
||||
unsafe {
|
||||
let mut out = MaybeUninit::uninit();
|
||||
expect_success(abi::get_tim(out.as_mut_ptr()), &"get_tim");
|
||||
out.assume_init()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,7 +76,7 @@ pub fn with_tmos_strong(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -
|
|||
// a problem in practice. (`u64::MAX` μs ≈ 584942 years)
|
||||
let ticks = dur.as_micros().min(abi::SYSTIM::MAX as u128) as abi::SYSTIM;
|
||||
|
||||
let start = Instant::now().0;
|
||||
let start = get_tim();
|
||||
let mut elapsed = 0;
|
||||
let mut er = abi::E_TMOUT;
|
||||
while elapsed <= ticks {
|
||||
|
|
@ -106,11 +84,8 @@ pub fn with_tmos_strong(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -
|
|||
if er != abi::E_TMOUT {
|
||||
break;
|
||||
}
|
||||
elapsed = Instant::now().0.wrapping_sub(start);
|
||||
elapsed = get_tim().wrapping_sub(start);
|
||||
}
|
||||
|
||||
er
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#![allow(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
pub mod os;
|
||||
pub mod time;
|
||||
|
||||
pub use moto_rt::futex;
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
pub use moto_rt::time::{Instant, SystemTime, UNIX_EPOCH};
|
||||
|
|
@ -12,7 +12,6 @@ pub mod abi;
|
|||
mod libunwind_integration;
|
||||
pub mod os;
|
||||
pub mod thread_parking;
|
||||
pub mod time;
|
||||
pub mod waitqueue;
|
||||
|
||||
// SAFETY: must be called only once during runtime initialization.
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ pub mod itron {
|
|||
pub(crate) mod error;
|
||||
pub mod os;
|
||||
pub use self::itron::thread_parking;
|
||||
pub mod time;
|
||||
|
||||
// SAFETY: must be called only once during runtime initialization.
|
||||
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
pub mod os;
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[path = "../unix/time.rs"]
|
||||
pub mod time;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,5 @@
|
|||
mod common;
|
||||
#[path = "../unsupported/os.rs"]
|
||||
pub mod os;
|
||||
#[path = "../unsupported/time.rs"]
|
||||
pub mod time;
|
||||
|
||||
pub use common::*;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
pub mod helpers;
|
||||
pub mod os;
|
||||
pub mod time;
|
||||
pub mod system_time;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
|||
151
library/std/src/sys/pal/uefi/system_time.rs
Normal file
151
library/std/src/sys/pal/uefi/system_time.rs
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
use r_efi::efi::{RuntimeServices, Time};
|
||||
|
||||
use super::helpers;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::ptr::NonNull;
|
||||
use crate::time::Duration;
|
||||
|
||||
const SECS_IN_MINUTE: u64 = 60;
|
||||
const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60;
|
||||
const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24;
|
||||
const SYSTEMTIME_TIMEZONE: i64 = -1440 * SECS_IN_MINUTE as i64;
|
||||
|
||||
pub(crate) fn now() -> Time {
|
||||
let runtime_services: NonNull<RuntimeServices> =
|
||||
helpers::runtime_services().expect("Runtime services are not available");
|
||||
let mut t: MaybeUninit<Time> = MaybeUninit::uninit();
|
||||
let r =
|
||||
unsafe { ((*runtime_services.as_ptr()).get_time)(t.as_mut_ptr(), crate::ptr::null_mut()) };
|
||||
if r.is_error() {
|
||||
panic!("time not implemented on this platform");
|
||||
}
|
||||
|
||||
unsafe { t.assume_init() }
|
||||
}
|
||||
|
||||
/// This algorithm is a modified form of the one described in the post
|
||||
/// https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html
|
||||
///
|
||||
/// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX
|
||||
/// epoch used in the original algorithm.
|
||||
pub(crate) const fn from_uefi(t: &Time) -> Option<Duration> {
|
||||
if !(t.month <= 12
|
||||
&& t.month != 0
|
||||
&& t.year >= 1900
|
||||
&& t.year <= 9999
|
||||
&& t.day <= 31
|
||||
&& t.day != 0
|
||||
&& t.second < 60
|
||||
&& t.minute <= 60
|
||||
&& t.hour < 24
|
||||
&& t.nanosecond < 1_000_000_000
|
||||
&& ((t.timezone <= 1440 && t.timezone >= -1440)
|
||||
|| t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE))
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
const YEAR_BASE: u32 = 4800; /* Before min year, multiple of 400. */
|
||||
|
||||
// Calculate the number of days since 1/1/1900. This is the earliest supported date in UEFI
|
||||
// time.
|
||||
// Use 1 March as the start
|
||||
let (m_adj, overflow): (u32, bool) = (t.month as u32).overflowing_sub(3);
|
||||
let (carry, adjust): (u32, u32) = if overflow { (1, 12) } else { (0, 0) };
|
||||
let y_adj: u32 = (t.year as u32) + YEAR_BASE - carry;
|
||||
let month_days: u32 = (m_adj.wrapping_add(adjust) * 62719 + 769) / 2048;
|
||||
let leap_days: u32 = y_adj / 4 - y_adj / 100 + y_adj / 400;
|
||||
let days: u32 = y_adj * 365 + leap_days + month_days + (t.day as u32 - 1) - 2447065;
|
||||
|
||||
let localtime_epoch: u64 = (days as u64) * SECS_IN_DAY
|
||||
+ (t.second as u64)
|
||||
+ (t.minute as u64) * SECS_IN_MINUTE
|
||||
+ (t.hour as u64) * SECS_IN_HOUR;
|
||||
|
||||
let normalized_timezone = if t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
|
||||
-SYSTEMTIME_TIMEZONE
|
||||
} else {
|
||||
(t.timezone as i64) * SECS_IN_MINUTE as i64 - SYSTEMTIME_TIMEZONE
|
||||
};
|
||||
|
||||
// Calculate the offset from 1/1/1900 at timezone -1440 min
|
||||
let epoch = localtime_epoch.checked_add_signed(normalized_timezone).unwrap();
|
||||
|
||||
Some(Duration::new(epoch, t.nanosecond))
|
||||
}
|
||||
|
||||
/// This algorithm is a modified version of the one described in the post:
|
||||
/// https://howardhinnant.github.io/date_algorithms.html#clive_from_days
|
||||
///
|
||||
/// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX
|
||||
/// epoch used in the original algorithm.
|
||||
pub(crate) const fn to_uefi(dur: &Duration, timezone: i16, daylight: u8) -> Result<Time, i16> {
|
||||
const MIN_IN_HOUR: u64 = 60;
|
||||
const MIN_IN_DAY: u64 = MIN_IN_HOUR * 24;
|
||||
|
||||
// Check timezone validity
|
||||
assert!(timezone <= 1440 && timezone >= -1440);
|
||||
|
||||
// Convert to seconds since 1900-01-01-00:00:00 in timezone.
|
||||
let Some(secs) = dur
|
||||
.as_secs()
|
||||
.checked_add_signed(SYSTEMTIME_TIMEZONE - (timezone as i64 * SECS_IN_MINUTE as i64))
|
||||
else {
|
||||
// If the current timezone cannot be used, find the closest timezone that will allow the
|
||||
// conversion to succeed.
|
||||
let new_tz = (dur.as_secs() / SECS_IN_MINUTE) as i16
|
||||
+ (SYSTEMTIME_TIMEZONE / SECS_IN_MINUTE as i64) as i16;
|
||||
return Err(new_tz);
|
||||
};
|
||||
|
||||
let days = secs / SECS_IN_DAY;
|
||||
let remaining_secs = secs % SECS_IN_DAY;
|
||||
|
||||
let z = days + 693901;
|
||||
let era = z / 146097;
|
||||
let doe = z - (era * 146097);
|
||||
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
||||
let mut y = yoe + era * 400;
|
||||
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
||||
let mp = (5 * doy + 2) / 153;
|
||||
let d = doy - (153 * mp + 2) / 5 + 1;
|
||||
let m = if mp < 10 { mp + 3 } else { mp - 9 };
|
||||
|
||||
if m <= 2 {
|
||||
y += 1;
|
||||
}
|
||||
|
||||
let hour = (remaining_secs / SECS_IN_HOUR) as u8;
|
||||
let minute = ((remaining_secs % SECS_IN_HOUR) / SECS_IN_MINUTE) as u8;
|
||||
let second = (remaining_secs % SECS_IN_MINUTE) as u8;
|
||||
|
||||
// At this point, invalid time will be greater than MAX representable time. It cannot be less
|
||||
// than minimum time since we already take care of that case above.
|
||||
if y <= 9999 {
|
||||
Ok(Time {
|
||||
year: y as u16,
|
||||
month: m as u8,
|
||||
day: d as u8,
|
||||
hour,
|
||||
minute,
|
||||
second,
|
||||
nanosecond: dur.subsec_nanos(),
|
||||
timezone,
|
||||
daylight,
|
||||
pad1: 0,
|
||||
pad2: 0,
|
||||
})
|
||||
} else {
|
||||
assert!(y == 10000);
|
||||
assert!(m == 1);
|
||||
|
||||
let delta = ((d - 1) as u64 * MIN_IN_DAY
|
||||
+ hour as u64 * MIN_IN_HOUR
|
||||
+ minute as u64
|
||||
+ if second == 0 { 0 } else { 1 }) as i16;
|
||||
let new_tz = timezone + delta;
|
||||
|
||||
assert!(new_tz <= 1440 && new_tz >= -1440);
|
||||
Err(new_tz)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
use core::num::niche_types::Nanoseconds;
|
||||
|
||||
use crate::sys::AsInner;
|
||||
use crate::io;
|
||||
use crate::time::Duration;
|
||||
use crate::{fmt, io};
|
||||
|
||||
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
|
||||
pub const TIMESPEC_MAX: libc::timespec =
|
||||
libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
|
||||
|
|
@ -18,60 +17,19 @@ pub(in crate::sys) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec {
|
|||
tv_nsec: (u64::MAX % NSEC_PER_SEC) as i64,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct SystemTime {
|
||||
pub(crate) t: Timespec,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub(crate) struct Timespec {
|
||||
tv_sec: i64,
|
||||
tv_nsec: Nanoseconds,
|
||||
}
|
||||
|
||||
impl SystemTime {
|
||||
pub const MAX: SystemTime = SystemTime { t: Timespec::MAX };
|
||||
|
||||
pub const MIN: SystemTime = SystemTime { t: Timespec::MIN };
|
||||
|
||||
#[cfg_attr(any(target_os = "horizon", target_os = "hurd"), allow(unused))]
|
||||
pub fn new(tv_sec: i64, tv_nsec: i64) -> Result<SystemTime, io::Error> {
|
||||
Ok(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)
|
||||
}
|
||||
|
||||
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 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()
|
||||
}
|
||||
pub tv_sec: i64,
|
||||
pub tv_nsec: Nanoseconds,
|
||||
}
|
||||
|
||||
impl Timespec {
|
||||
const MAX: Timespec = unsafe { Self::new_unchecked(i64::MAX, 1_000_000_000 - 1) };
|
||||
pub const MAX: Timespec = unsafe { Self::new_unchecked(i64::MAX, 1_000_000_000 - 1) };
|
||||
|
||||
// As described below, on Apple OS, dates before epoch are represented differently.
|
||||
// This is not an issue here however, because we are using tv_sec = i64::MIN,
|
||||
// which will cause the compatibility wrapper to not be executed at all.
|
||||
const MIN: Timespec = unsafe { Self::new_unchecked(i64::MIN, 0) };
|
||||
pub const MIN: Timespec = unsafe { Self::new_unchecked(i64::MIN, 0) };
|
||||
|
||||
const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec {
|
||||
Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds::new_unchecked(tv_nsec as u32) } }
|
||||
|
|
@ -81,7 +39,7 @@ impl Timespec {
|
|||
unsafe { Self::new_unchecked(0, 0) }
|
||||
}
|
||||
|
||||
const fn new(tv_sec: i64, tv_nsec: i64) -> Result<Timespec, io::Error> {
|
||||
pub const fn new(tv_sec: i64, tv_nsec: i64) -> Result<Timespec, io::Error> {
|
||||
// On Apple OS, dates before epoch are represented differently than on other
|
||||
// Unix platforms: e.g. 1/10th of a second before epoch is represented as `seconds=-1`
|
||||
// and `nanoseconds=100_000_000` on other platforms, but is `seconds=0` and
|
||||
|
|
@ -263,98 +221,3 @@ impl __timespec64 {
|
|||
Self { tv_sec, tv_nsec, _padding: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Instant {
|
||||
t: Timespec,
|
||||
}
|
||||
|
||||
impl Instant {
|
||||
// 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.
|
||||
//
|
||||
// We use `CLOCK_UPTIME_RAW` instead of `CLOCK_MONOTONIC` since
|
||||
// `CLOCK_UPTIME_RAW` is based on `mach_absolute_time`, which is the
|
||||
// clock that all timeouts and deadlines are measured against inside
|
||||
// the kernel.
|
||||
#[cfg(target_vendor = "apple")]
|
||||
pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_UPTIME_RAW;
|
||||
|
||||
#[cfg(not(target_vendor = "apple"))]
|
||||
pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_MONOTONIC;
|
||||
|
||||
pub fn now() -> Instant {
|
||||
// https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html
|
||||
Instant { t: Timespec::now(Self::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)? })
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
not(target_os = "linux"),
|
||||
allow(unused, reason = "needed by the `sleep_until` on some unix platforms")
|
||||
)]
|
||||
pub(crate) fn into_timespec(self) -> Timespec {
|
||||
self.t
|
||||
}
|
||||
|
||||
/// Returns `self` converted into units of `mach_absolute_time`, or `None`
|
||||
/// if `self` is before the system boot time. If the conversion cannot be
|
||||
/// performed precisely, this ceils the result up to the nearest
|
||||
/// representable value.
|
||||
#[cfg(target_vendor = "apple")]
|
||||
pub fn into_mach_absolute_time_ceil(self) -> Option<u128> {
|
||||
#[repr(C)]
|
||||
struct mach_timebase_info {
|
||||
numer: u32,
|
||||
denom: u32,
|
||||
}
|
||||
|
||||
unsafe extern "C" {
|
||||
unsafe fn mach_timebase_info(info: *mut mach_timebase_info) -> libc::kern_return_t;
|
||||
}
|
||||
|
||||
let secs = u64::try_from(self.t.tv_sec).ok()?;
|
||||
|
||||
let mut timebase = mach_timebase_info { numer: 0, denom: 0 };
|
||||
assert_eq!(unsafe { mach_timebase_info(&mut timebase) }, libc::KERN_SUCCESS);
|
||||
|
||||
// Since `tv_sec` is 64-bit and `tv_nsec` is smaller than 1 billion,
|
||||
// this cannot overflow. The resulting number needs at most 94 bits.
|
||||
let nanos =
|
||||
u128::from(secs) * u128::from(NSEC_PER_SEC) + u128::from(self.t.tv_nsec.as_inner());
|
||||
// This multiplication cannot overflow since multiplying a 94-bit
|
||||
// number by a 32-bit number yields a number that needs at most
|
||||
// 126 bits.
|
||||
Some((nanos * u128::from(timebase.denom)).div_ceil(u128::from(timebase.numer)))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsInner<Timespec> for Instant {
|
||||
fn as_inner(&self) -> &Timespec {
|
||||
&self.t
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
pub mod os;
|
||||
pub mod time;
|
||||
|
||||
mod common;
|
||||
pub use common::*;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
pub mod os;
|
||||
pub mod time;
|
||||
|
||||
#[expect(dead_code)]
|
||||
#[path = "../unsupported/common.rs"]
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@
|
|||
|
||||
#[path = "../unsupported/os.rs"]
|
||||
pub mod os;
|
||||
#[path = "../unsupported/time.rs"]
|
||||
pub mod time;
|
||||
|
||||
#[cfg(target_feature = "atomics")]
|
||||
#[path = "atomics/futex.rs"]
|
||||
|
|
|
|||
|
|
@ -1,170 +1,12 @@
|
|||
use core::hash::{Hash, Hasher};
|
||||
use core::ops::Neg;
|
||||
|
||||
use crate::cmp::Ordering;
|
||||
use crate::ops::Neg;
|
||||
use crate::ptr::null;
|
||||
use crate::sys::{IntoInner, c};
|
||||
use crate::sys::pal::c;
|
||||
use crate::time::Duration;
|
||||
use crate::{fmt, mem};
|
||||
|
||||
const NANOS_PER_SEC: u64 = 1_000_000_000;
|
||||
const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100;
|
||||
pub const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
|
||||
pub struct Instant {
|
||||
// This duration is relative to an arbitrary microsecond epoch
|
||||
// from the winapi QueryPerformanceCounter function.
|
||||
t: Duration,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SystemTime {
|
||||
t: c::FILETIME,
|
||||
}
|
||||
|
||||
const INTERVALS_TO_UNIX_EPOCH: u64 = 11_644_473_600 * INTERVALS_PER_SEC;
|
||||
|
||||
pub const UNIX_EPOCH: SystemTime = SystemTime {
|
||||
t: c::FILETIME {
|
||||
dwLowDateTime: INTERVALS_TO_UNIX_EPOCH as u32,
|
||||
dwHighDateTime: (INTERVALS_TO_UNIX_EPOCH >> 32) as u32,
|
||||
},
|
||||
};
|
||||
|
||||
impl Instant {
|
||||
pub fn now() -> Instant {
|
||||
// High precision timing on windows operates in "Performance Counter"
|
||||
// units, as returned by the WINAPI QueryPerformanceCounter function.
|
||||
// These relate to seconds by a factor of QueryPerformanceFrequency.
|
||||
// In order to keep unit conversions out of normal interval math, we
|
||||
// measure in QPC units and immediately convert to nanoseconds.
|
||||
perf_counter::PerformanceCounterInstant::now().into()
|
||||
}
|
||||
|
||||
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
|
||||
// On windows there's a threshold below which we consider two timestamps
|
||||
// equivalent due to measurement error. For more details + doc link,
|
||||
// check the docs on epsilon.
|
||||
let epsilon = perf_counter::PerformanceCounterInstant::epsilon();
|
||||
if other.t > self.t && other.t - self.t <= epsilon {
|
||||
Some(Duration::new(0, 0))
|
||||
} else {
|
||||
self.t.checked_sub(other.t)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
|
||||
Some(Instant { t: self.t.checked_add(*other)? })
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
|
||||
Some(Instant { t: self.t.checked_sub(*other)? })
|
||||
}
|
||||
}
|
||||
|
||||
impl SystemTime {
|
||||
pub const MAX: SystemTime = SystemTime {
|
||||
t: c::FILETIME {
|
||||
dwLowDateTime: (i64::MAX & 0xFFFFFFFF) as u32,
|
||||
dwHighDateTime: (i64::MAX >> 32) as u32,
|
||||
},
|
||||
};
|
||||
|
||||
pub const MIN: SystemTime =
|
||||
SystemTime { t: c::FILETIME { dwLowDateTime: 0, dwHighDateTime: 0 } };
|
||||
|
||||
pub fn now() -> SystemTime {
|
||||
unsafe {
|
||||
let mut t: SystemTime = mem::zeroed();
|
||||
c::GetSystemTimePreciseAsFileTime(&mut t.t);
|
||||
t
|
||||
}
|
||||
}
|
||||
|
||||
fn from_intervals(intervals: i64) -> SystemTime {
|
||||
SystemTime {
|
||||
t: c::FILETIME {
|
||||
dwLowDateTime: intervals as u32,
|
||||
dwHighDateTime: (intervals >> 32) as u32,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn intervals(&self) -> i64 {
|
||||
(self.t.dwLowDateTime as i64) | ((self.t.dwHighDateTime as i64) << 32)
|
||||
}
|
||||
|
||||
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
|
||||
let me = self.intervals();
|
||||
let other = other.intervals();
|
||||
if me >= other {
|
||||
Ok(intervals2dur((me - other) as u64))
|
||||
} else {
|
||||
Err(intervals2dur((other - me) as u64))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
let intervals = self.intervals().checked_add(checked_dur2intervals(other)?)?;
|
||||
Some(SystemTime::from_intervals(intervals))
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
// Windows does not support times before 1601, hence why we don't
|
||||
// support negatives. In order to tackle this, we try to convert the
|
||||
// resulting value into an u64, which should obviously fail in the case
|
||||
// that the value is below zero.
|
||||
let intervals: u64 =
|
||||
self.intervals().checked_sub(checked_dur2intervals(other)?)?.try_into().ok()?;
|
||||
Some(SystemTime::from_intervals(intervals as i64))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for SystemTime {
|
||||
fn eq(&self, other: &SystemTime) -> bool {
|
||||
self.intervals() == other.intervals()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for SystemTime {}
|
||||
|
||||
impl PartialOrd for SystemTime {
|
||||
fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for SystemTime {
|
||||
fn cmp(&self, other: &SystemTime) -> Ordering {
|
||||
self.intervals().cmp(&other.intervals())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SystemTime {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("SystemTime").field("intervals", &self.intervals()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<c::FILETIME> for SystemTime {
|
||||
fn from(t: c::FILETIME) -> SystemTime {
|
||||
SystemTime { t }
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoInner<c::FILETIME> for SystemTime {
|
||||
fn into_inner(self) -> c::FILETIME {
|
||||
self.t
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for SystemTime {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.intervals().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
fn checked_dur2intervals(dur: &Duration) -> Option<i64> {
|
||||
pub fn checked_dur2intervals(dur: &Duration) -> Option<i64> {
|
||||
dur.as_secs()
|
||||
.checked_mul(INTERVALS_PER_SEC)?
|
||||
.checked_add(dur.subsec_nanos() as u64 / 100)?
|
||||
|
|
@ -172,43 +14,23 @@ fn checked_dur2intervals(dur: &Duration) -> Option<i64> {
|
|||
.ok()
|
||||
}
|
||||
|
||||
fn intervals2dur(intervals: u64) -> Duration {
|
||||
pub fn intervals2dur(intervals: u64) -> Duration {
|
||||
Duration::new(intervals / INTERVALS_PER_SEC, ((intervals % INTERVALS_PER_SEC) * 100) as u32)
|
||||
}
|
||||
|
||||
mod perf_counter {
|
||||
pub mod perf_counter {
|
||||
use super::NANOS_PER_SEC;
|
||||
use crate::sync::atomic::{Atomic, AtomicU64, Ordering};
|
||||
use crate::sys::helpers::mul_div_u64;
|
||||
use crate::sys::{c, cvt};
|
||||
use crate::time::Duration;
|
||||
|
||||
pub struct PerformanceCounterInstant {
|
||||
ts: i64,
|
||||
}
|
||||
impl PerformanceCounterInstant {
|
||||
pub fn now() -> Self {
|
||||
Self { ts: query() }
|
||||
}
|
||||
|
||||
// Per microsoft docs, the margin of error for cross-thread time comparisons
|
||||
// using QueryPerformanceCounter is 1 "tick" -- defined as 1/frequency().
|
||||
// Reference: https://docs.microsoft.com/en-us/windows/desktop/SysInfo
|
||||
// /acquiring-high-resolution-time-stamps
|
||||
pub fn epsilon() -> Duration {
|
||||
let epsilon = NANOS_PER_SEC / (frequency() as u64);
|
||||
Duration::from_nanos(epsilon)
|
||||
}
|
||||
}
|
||||
impl From<PerformanceCounterInstant> for super::Instant {
|
||||
fn from(other: PerformanceCounterInstant) -> Self {
|
||||
let freq = frequency() as u64;
|
||||
let instant_nsec = mul_div_u64(other.ts as u64, NANOS_PER_SEC, freq);
|
||||
Self { t: Duration::from_nanos(instant_nsec) }
|
||||
}
|
||||
pub fn now() -> i64 {
|
||||
let mut qpc_value: i64 = 0;
|
||||
cvt(unsafe { c::QueryPerformanceCounter(&mut qpc_value) }).unwrap();
|
||||
qpc_value
|
||||
}
|
||||
|
||||
fn frequency() -> i64 {
|
||||
pub fn frequency() -> i64 {
|
||||
// Either the cached result of `QueryPerformanceFrequency` or `0` for
|
||||
// uninitialized. Storing this as a single `AtomicU64` allows us to use
|
||||
// `Relaxed` operations, as we are only interested in the effects on a
|
||||
|
|
@ -230,10 +52,13 @@ mod perf_counter {
|
|||
frequency
|
||||
}
|
||||
|
||||
fn query() -> i64 {
|
||||
let mut qpc_value: i64 = 0;
|
||||
cvt(unsafe { c::QueryPerformanceCounter(&mut qpc_value) }).unwrap();
|
||||
qpc_value
|
||||
// Per microsoft docs, the margin of error for cross-thread time comparisons
|
||||
// using QueryPerformanceCounter is 1 "tick" -- defined as 1/frequency().
|
||||
// Reference: https://docs.microsoft.com/en-us/windows/desktop/SysInfo
|
||||
// /acquiring-high-resolution-time-stamps
|
||||
pub fn epsilon() -> Duration {
|
||||
let epsilon = NANOS_PER_SEC / (frequency() as u64);
|
||||
Duration::from_nanos(epsilon)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -241,6 +66,7 @@ mod perf_counter {
|
|||
pub(crate) struct WaitableTimer {
|
||||
handle: c::HANDLE,
|
||||
}
|
||||
|
||||
impl WaitableTimer {
|
||||
/// Creates a high-resolution timer. Will fail before Windows 10, version 1803.
|
||||
pub fn high_resolution() -> Result<Self, ()> {
|
||||
|
|
@ -254,6 +80,7 @@ impl WaitableTimer {
|
|||
};
|
||||
if !handle.is_null() { Ok(Self { handle }) } else { Err(()) }
|
||||
}
|
||||
|
||||
pub fn set(&self, duration: Duration) -> Result<(), ()> {
|
||||
// Convert the Duration to a format similar to FILETIME.
|
||||
// Negative values are relative times whereas positive values are absolute.
|
||||
|
|
@ -262,11 +89,13 @@ impl WaitableTimer {
|
|||
let result = unsafe { c::SetWaitableTimer(self.handle, &time, 0, None, null(), c::FALSE) };
|
||||
if result != 0 { Ok(()) } else { Err(()) }
|
||||
}
|
||||
|
||||
pub fn wait(&self) -> Result<(), ()> {
|
||||
let result = unsafe { c::WaitForSingleObject(self.handle, c::INFINITE) };
|
||||
if result != c::WAIT_FAILED { Ok(()) } else { Err(()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WaitableTimer {
|
||||
fn drop(&mut self) {
|
||||
unsafe { c::CloseHandle(self.handle) };
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
pub mod os;
|
||||
pub mod time;
|
||||
|
||||
#[path = "../unsupported/common.rs"]
|
||||
mod common;
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@ pub const WORD_SIZE: usize = size_of::<u32>();
|
|||
|
||||
pub mod abi;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/time.rs"]
|
||||
pub mod time;
|
||||
|
||||
use crate::io as std_io;
|
||||
|
||||
|
|
|
|||
63
library/std/src/sys/time/hermit.rs
Normal file
63
library/std/src/sys/time/hermit.rs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
use hermit_abi::{self, CLOCK_MONOTONIC, CLOCK_REALTIME};
|
||||
|
||||
use crate::hash::Hash;
|
||||
use crate::sys::pal::time::Timespec;
|
||||
use crate::time::Duration;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||
pub struct Instant(Timespec);
|
||||
|
||||
impl Instant {
|
||||
pub fn now() -> Instant {
|
||||
let mut time: Timespec = Timespec::zero();
|
||||
let _ = unsafe { hermit_abi::clock_gettime(CLOCK_MONOTONIC, &raw mut time.t) };
|
||||
|
||||
Instant(time)
|
||||
}
|
||||
|
||||
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
|
||||
self.0.sub_timespec(&other.0).ok()
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
|
||||
Some(Instant(self.0.checked_add_duration(other)?))
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
|
||||
Some(Instant(self.0.checked_sub_duration(other)?))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub struct SystemTime(Timespec);
|
||||
|
||||
pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero());
|
||||
|
||||
impl SystemTime {
|
||||
pub const MAX: SystemTime = SystemTime(Timespec::MAX);
|
||||
|
||||
pub const MIN: SystemTime = SystemTime(Timespec::MIN);
|
||||
|
||||
pub fn new(tv_sec: i64, tv_nsec: i32) -> SystemTime {
|
||||
SystemTime(Timespec::new(tv_sec, tv_nsec))
|
||||
}
|
||||
|
||||
pub fn now() -> SystemTime {
|
||||
let mut time: Timespec = Timespec::zero();
|
||||
let _ = unsafe { hermit_abi::clock_gettime(CLOCK_REALTIME, &raw mut time.t) };
|
||||
|
||||
SystemTime(time)
|
||||
}
|
||||
|
||||
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
|
||||
self.0.sub_timespec(&other.0)
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
Some(SystemTime(self.0.checked_add_duration(other)?))
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
Some(SystemTime(self.0.checked_sub_duration(other)?))
|
||||
}
|
||||
}
|
||||
53
library/std/src/sys/time/mod.rs
Normal file
53
library/std/src/sys/time/mod.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
cfg_select! {
|
||||
target_os = "hermit" => {
|
||||
mod hermit;
|
||||
use hermit as imp;
|
||||
}
|
||||
target_os = "motor" => {
|
||||
use moto_rt::time as imp;
|
||||
}
|
||||
all(target_vendor = "fortanix", target_env = "sgx") => {
|
||||
mod sgx;
|
||||
use sgx as imp;
|
||||
}
|
||||
target_os = "solid_asp3" => {
|
||||
mod solid;
|
||||
use solid as imp;
|
||||
}
|
||||
target_os = "uefi" => {
|
||||
mod uefi;
|
||||
use uefi as imp;
|
||||
}
|
||||
any(
|
||||
target_os = "teeos",
|
||||
target_family = "unix",
|
||||
target_os = "wasi",
|
||||
) => {
|
||||
mod unix;
|
||||
use unix as imp;
|
||||
}
|
||||
target_os = "vexos" => {
|
||||
mod vexos;
|
||||
#[expect(unused)]
|
||||
mod unsupported;
|
||||
|
||||
mod imp {
|
||||
pub use super::vexos::Instant;
|
||||
pub use super::unsupported::{SystemTime, UNIX_EPOCH};
|
||||
}
|
||||
}
|
||||
target_os = "windows" => {
|
||||
mod windows;
|
||||
use windows as imp;
|
||||
}
|
||||
target_os = "xous" => {
|
||||
mod xous;
|
||||
use xous as imp;
|
||||
}
|
||||
_ => {
|
||||
mod unsupported;
|
||||
use unsupported as imp;
|
||||
}
|
||||
}
|
||||
|
||||
pub use imp::{Instant, SystemTime, UNIX_EPOCH};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
use super::abi::usercalls;
|
||||
use crate::sys::pal::abi::usercalls;
|
||||
use crate::time::Duration;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||
|
|
@ -1,9 +1,38 @@
|
|||
use super::abi;
|
||||
use super::error::expect_success;
|
||||
pub use super::itron::time::Instant;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::sys::pal::error::expect_success;
|
||||
use crate::sys::pal::{abi, itron};
|
||||
use crate::time::Duration;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||
pub struct Instant(itron::abi::SYSTIM);
|
||||
|
||||
impl Instant {
|
||||
pub fn now() -> Instant {
|
||||
Instant(itron::time::get_tim())
|
||||
}
|
||||
|
||||
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
|
||||
self.0.checked_sub(other.0).map(|ticks| {
|
||||
// `SYSTIM` is measured in microseconds
|
||||
Duration::from_micros(ticks)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
|
||||
// `SYSTIM` is measured in microseconds
|
||||
let ticks = other.as_micros();
|
||||
|
||||
Some(Instant(self.0.checked_add(ticks.try_into().ok()?)?))
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
|
||||
// `SYSTIM` is measured in microseconds
|
||||
let ticks = other.as_micros();
|
||||
|
||||
Some(Instant(self.0.checked_sub(ticks.try_into().ok()?)?))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||
pub struct SystemTime(abi::time_t);
|
||||
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
use crate::sys::pal::system_time;
|
||||
use crate::time::Duration;
|
||||
|
||||
const SECS_IN_MINUTE: u64 = 60;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||
pub struct Instant(Duration);
|
||||
|
||||
|
|
@ -88,7 +87,7 @@ impl SystemTime {
|
|||
.unwrap();
|
||||
|
||||
pub(crate) const fn from_uefi(t: r_efi::efi::Time) -> Option<Self> {
|
||||
match system_time_internal::from_uefi(&t) {
|
||||
match system_time::from_uefi(&t) {
|
||||
Some(x) => Some(Self(x)),
|
||||
None => None,
|
||||
}
|
||||
|
|
@ -99,12 +98,12 @@ impl SystemTime {
|
|||
timezone: i16,
|
||||
daylight: u8,
|
||||
) -> Result<r_efi::efi::Time, i16> {
|
||||
// system_time_internal::to_uefi requires a valid timezone. In case of unspecified timezone,
|
||||
// system_time::to_uefi requires a valid timezone. In case of unspecified timezone,
|
||||
// we just pass 0 since it is assumed that no timezone related adjustments are required.
|
||||
if timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
|
||||
system_time_internal::to_uefi(&self.0, 0, daylight)
|
||||
system_time::to_uefi(&self.0, 0, daylight)
|
||||
} else {
|
||||
system_time_internal::to_uefi(&self.0, timezone, daylight)
|
||||
system_time::to_uefi(&self.0, timezone, daylight)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -118,8 +117,7 @@ impl SystemTime {
|
|||
}
|
||||
|
||||
pub fn now() -> SystemTime {
|
||||
Self::from_uefi(system_time_internal::now())
|
||||
.expect("time incorrectly implemented on this platform")
|
||||
Self::from_uefi(system_time::now()).expect("time incorrectly implemented on this platform")
|
||||
}
|
||||
|
||||
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
|
||||
|
|
@ -138,169 +136,15 @@ impl SystemTime {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) mod system_time_internal {
|
||||
use r_efi::efi::{RuntimeServices, Time};
|
||||
|
||||
use super::super::helpers;
|
||||
use super::*;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::ptr::NonNull;
|
||||
|
||||
const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60;
|
||||
const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24;
|
||||
const SYSTEMTIME_TIMEZONE: i64 = -1440 * SECS_IN_MINUTE as i64;
|
||||
|
||||
pub(crate) fn now() -> Time {
|
||||
let runtime_services: NonNull<RuntimeServices> =
|
||||
helpers::runtime_services().expect("Runtime services are not available");
|
||||
let mut t: MaybeUninit<Time> = MaybeUninit::uninit();
|
||||
let r = unsafe {
|
||||
((*runtime_services.as_ptr()).get_time)(t.as_mut_ptr(), crate::ptr::null_mut())
|
||||
};
|
||||
if r.is_error() {
|
||||
panic!("time not implemented on this platform");
|
||||
}
|
||||
|
||||
unsafe { t.assume_init() }
|
||||
}
|
||||
|
||||
/// This algorithm is a modified form of the one described in the post
|
||||
/// https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html
|
||||
///
|
||||
/// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX
|
||||
/// epoch used in the original algorithm.
|
||||
pub(crate) const fn from_uefi(t: &Time) -> Option<Duration> {
|
||||
if !(t.month <= 12
|
||||
&& t.month != 0
|
||||
&& t.year >= 1900
|
||||
&& t.year <= 9999
|
||||
&& t.day <= 31
|
||||
&& t.day != 0
|
||||
&& t.second < 60
|
||||
&& t.minute <= 60
|
||||
&& t.hour < 24
|
||||
&& t.nanosecond < 1_000_000_000
|
||||
&& ((t.timezone <= 1440 && t.timezone >= -1440)
|
||||
|| t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE))
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
const YEAR_BASE: u32 = 4800; /* Before min year, multiple of 400. */
|
||||
|
||||
// Calculate the number of days since 1/1/1900. This is the earliest supported date in UEFI
|
||||
// time.
|
||||
// Use 1 March as the start
|
||||
let (m_adj, overflow): (u32, bool) = (t.month as u32).overflowing_sub(3);
|
||||
let (carry, adjust): (u32, u32) = if overflow { (1, 12) } else { (0, 0) };
|
||||
let y_adj: u32 = (t.year as u32) + YEAR_BASE - carry;
|
||||
let month_days: u32 = (m_adj.wrapping_add(adjust) * 62719 + 769) / 2048;
|
||||
let leap_days: u32 = y_adj / 4 - y_adj / 100 + y_adj / 400;
|
||||
let days: u32 = y_adj * 365 + leap_days + month_days + (t.day as u32 - 1) - 2447065;
|
||||
|
||||
let localtime_epoch: u64 = (days as u64) * SECS_IN_DAY
|
||||
+ (t.second as u64)
|
||||
+ (t.minute as u64) * SECS_IN_MINUTE
|
||||
+ (t.hour as u64) * SECS_IN_HOUR;
|
||||
|
||||
let normalized_timezone = if t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
|
||||
-SYSTEMTIME_TIMEZONE
|
||||
} else {
|
||||
(t.timezone as i64) * SECS_IN_MINUTE as i64 - SYSTEMTIME_TIMEZONE
|
||||
};
|
||||
|
||||
// Calculate the offset from 1/1/1900 at timezone -1440 min
|
||||
let epoch = localtime_epoch.checked_add_signed(normalized_timezone).unwrap();
|
||||
|
||||
Some(Duration::new(epoch, t.nanosecond))
|
||||
}
|
||||
|
||||
/// This algorithm is a modified version of the one described in the post:
|
||||
/// https://howardhinnant.github.io/date_algorithms.html#clive_from_days
|
||||
///
|
||||
/// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX
|
||||
/// epoch used in the original algorithm.
|
||||
pub(crate) const fn to_uefi(dur: &Duration, timezone: i16, daylight: u8) -> Result<Time, i16> {
|
||||
const MIN_IN_HOUR: u64 = 60;
|
||||
const MIN_IN_DAY: u64 = MIN_IN_HOUR * 24;
|
||||
|
||||
// Check timezone validity
|
||||
assert!(timezone <= 1440 && timezone >= -1440);
|
||||
|
||||
// Convert to seconds since 1900-01-01-00:00:00 in timezone.
|
||||
let Some(secs) = dur
|
||||
.as_secs()
|
||||
.checked_add_signed(SYSTEMTIME_TIMEZONE - (timezone as i64 * SECS_IN_MINUTE as i64))
|
||||
else {
|
||||
// If the current timezone cannot be used, find the closest timezone that will allow the
|
||||
// conversion to succeed.
|
||||
let new_tz = (dur.as_secs() / SECS_IN_MINUTE) as i16
|
||||
+ (SYSTEMTIME_TIMEZONE / SECS_IN_MINUTE as i64) as i16;
|
||||
return Err(new_tz);
|
||||
};
|
||||
|
||||
let days = secs / SECS_IN_DAY;
|
||||
let remaining_secs = secs % SECS_IN_DAY;
|
||||
|
||||
let z = days + 693901;
|
||||
let era = z / 146097;
|
||||
let doe = z - (era * 146097);
|
||||
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
||||
let mut y = yoe + era * 400;
|
||||
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
||||
let mp = (5 * doy + 2) / 153;
|
||||
let d = doy - (153 * mp + 2) / 5 + 1;
|
||||
let m = if mp < 10 { mp + 3 } else { mp - 9 };
|
||||
|
||||
if m <= 2 {
|
||||
y += 1;
|
||||
}
|
||||
|
||||
let hour = (remaining_secs / SECS_IN_HOUR) as u8;
|
||||
let minute = ((remaining_secs % SECS_IN_HOUR) / SECS_IN_MINUTE) as u8;
|
||||
let second = (remaining_secs % SECS_IN_MINUTE) as u8;
|
||||
|
||||
// At this point, invalid time will be greater than MAX representable time. It cannot be less
|
||||
// than minimum time since we already take care of that case above.
|
||||
if y <= 9999 {
|
||||
Ok(Time {
|
||||
year: y as u16,
|
||||
month: m as u8,
|
||||
day: d as u8,
|
||||
hour,
|
||||
minute,
|
||||
second,
|
||||
nanosecond: dur.subsec_nanos(),
|
||||
timezone,
|
||||
daylight,
|
||||
pad1: 0,
|
||||
pad2: 0,
|
||||
})
|
||||
} else {
|
||||
assert!(y == 10000);
|
||||
assert!(m == 1);
|
||||
|
||||
let delta = ((d - 1) as u64 * MIN_IN_DAY
|
||||
+ hour as u64 * MIN_IN_HOUR
|
||||
+ minute as u64
|
||||
+ if second == 0 { 0 } else { 1 }) as i16;
|
||||
let new_tz = timezone + delta;
|
||||
|
||||
assert!(new_tz <= 1440 && new_tz >= -1440);
|
||||
Err(new_tz)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod instant_internal {
|
||||
mod instant_internal {
|
||||
use r_efi::protocols::timestamp;
|
||||
|
||||
use super::super::helpers;
|
||||
use super::*;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::ptr::NonNull;
|
||||
use crate::sync::atomic::{Atomic, AtomicPtr, Ordering};
|
||||
use crate::sys::helpers::mul_div_u64;
|
||||
use crate::sys::pal::helpers;
|
||||
|
||||
const NS_PER_SEC: u64 = 1_000_000_000;
|
||||
|
||||
141
library/std/src/sys/time/unix.rs
Normal file
141
library/std/src/sys/time/unix.rs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
use crate::sys::AsInner;
|
||||
use crate::sys::pal::time::Timespec;
|
||||
use crate::time::Duration;
|
||||
use crate::{fmt, io};
|
||||
|
||||
pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct SystemTime {
|
||||
pub(crate) t: Timespec,
|
||||
}
|
||||
|
||||
impl SystemTime {
|
||||
pub const MAX: SystemTime = SystemTime { t: Timespec::MAX };
|
||||
|
||||
pub const MIN: SystemTime = SystemTime { t: Timespec::MIN };
|
||||
|
||||
#[cfg_attr(any(target_os = "horizon", target_os = "hurd", target_os = "teeos"), expect(unused))]
|
||||
pub fn new(tv_sec: i64, tv_nsec: i64) -> Result<SystemTime, io::Error> {
|
||||
Ok(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)
|
||||
}
|
||||
|
||||
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 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()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Instant {
|
||||
t: Timespec,
|
||||
}
|
||||
|
||||
impl Instant {
|
||||
// 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.
|
||||
//
|
||||
// We use `CLOCK_UPTIME_RAW` instead of `CLOCK_MONOTONIC` since
|
||||
// `CLOCK_UPTIME_RAW` is based on `mach_absolute_time`, which is the
|
||||
// clock that all timeouts and deadlines are measured against inside
|
||||
// the kernel.
|
||||
#[cfg(target_vendor = "apple")]
|
||||
pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_UPTIME_RAW;
|
||||
|
||||
#[cfg(not(target_vendor = "apple"))]
|
||||
pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_MONOTONIC;
|
||||
|
||||
pub fn now() -> Instant {
|
||||
// https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html
|
||||
Instant { t: Timespec::now(Self::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)? })
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
not(target_os = "linux"),
|
||||
allow(unused, reason = "needed by the `sleep_until` on some unix platforms")
|
||||
)]
|
||||
pub(crate) fn into_timespec(self) -> Timespec {
|
||||
self.t
|
||||
}
|
||||
|
||||
/// Returns `self` converted into units of `mach_absolute_time`, or `None`
|
||||
/// if `self` is before the system boot time. If the conversion cannot be
|
||||
/// performed precisely, this ceils the result up to the nearest
|
||||
/// representable value.
|
||||
#[cfg(target_vendor = "apple")]
|
||||
pub fn into_mach_absolute_time_ceil(self) -> Option<u128> {
|
||||
#[repr(C)]
|
||||
struct mach_timebase_info {
|
||||
numer: u32,
|
||||
denom: u32,
|
||||
}
|
||||
|
||||
unsafe extern "C" {
|
||||
unsafe fn mach_timebase_info(info: *mut mach_timebase_info) -> libc::kern_return_t;
|
||||
}
|
||||
|
||||
let secs = u64::try_from(self.t.tv_sec).ok()?;
|
||||
|
||||
let mut timebase = mach_timebase_info { numer: 0, denom: 0 };
|
||||
assert_eq!(unsafe { mach_timebase_info(&mut timebase) }, libc::KERN_SUCCESS);
|
||||
|
||||
// Since `tv_sec` is 64-bit and `tv_nsec` is smaller than 1 billion,
|
||||
// this cannot overflow. The resulting number needs at most 94 bits.
|
||||
let nanos = 1_000_000_000 * u128::from(secs) + u128::from(self.t.tv_nsec.as_inner());
|
||||
// This multiplication cannot overflow since multiplying a 94-bit
|
||||
// number by a 32-bit number yields a number that needs at most
|
||||
// 126 bits.
|
||||
Some((nanos * u128::from(timebase.denom)).div_ceil(u128::from(timebase.numer)))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsInner<Timespec> for Instant {
|
||||
fn as_inner(&self) -> &Timespec {
|
||||
&self.t
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,5 @@
|
|||
use crate::time::Duration;
|
||||
|
||||
#[expect(dead_code)]
|
||||
#[path = "../unsupported/time.rs"]
|
||||
mod unsupported_time;
|
||||
pub use unsupported_time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||
pub struct Instant(Duration);
|
||||
|
||||
156
library/std/src/sys/time/windows.rs
Normal file
156
library/std/src/sys/time/windows.rs
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
use crate::cmp::Ordering;
|
||||
use crate::hash::{Hash, Hasher};
|
||||
use crate::sys::helpers::mul_div_u64;
|
||||
use crate::sys::pal::time::{
|
||||
INTERVALS_PER_SEC, checked_dur2intervals, intervals2dur, perf_counter,
|
||||
};
|
||||
use crate::sys::{IntoInner, c};
|
||||
use crate::time::Duration;
|
||||
use crate::{fmt, mem};
|
||||
|
||||
const NANOS_PER_SEC: u64 = 1_000_000_000;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
|
||||
pub struct Instant {
|
||||
// This duration is relative to an arbitrary microsecond epoch
|
||||
// from the winapi QueryPerformanceCounter function.
|
||||
t: Duration,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SystemTime {
|
||||
t: c::FILETIME,
|
||||
}
|
||||
|
||||
pub const UNIX_EPOCH: SystemTime =
|
||||
SystemTime::from_intervals(11_644_473_600 * INTERVALS_PER_SEC as i64);
|
||||
|
||||
impl Instant {
|
||||
pub fn now() -> Instant {
|
||||
// High precision timing on windows operates in "Performance Counter"
|
||||
// units, as returned by the WINAPI QueryPerformanceCounter function.
|
||||
// These relate to seconds by a factor of QueryPerformanceFrequency.
|
||||
// In order to keep unit conversions out of normal interval math, we
|
||||
// measure in QPC units and immediately convert to nanoseconds.
|
||||
|
||||
let freq = perf_counter::frequency() as u64;
|
||||
let now = perf_counter::now();
|
||||
let instant_nsec = mul_div_u64(now as u64, NANOS_PER_SEC, freq);
|
||||
Self { t: Duration::from_nanos(instant_nsec) }
|
||||
}
|
||||
|
||||
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
|
||||
// On windows there's a threshold below which we consider two timestamps
|
||||
// equivalent due to measurement error. For more details + doc link,
|
||||
// check the docs on epsilon.
|
||||
let epsilon = perf_counter::epsilon();
|
||||
if other.t > self.t && other.t - self.t <= epsilon {
|
||||
Some(Duration::new(0, 0))
|
||||
} else {
|
||||
self.t.checked_sub(other.t)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
|
||||
Some(Instant { t: self.t.checked_add(*other)? })
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
|
||||
Some(Instant { t: self.t.checked_sub(*other)? })
|
||||
}
|
||||
}
|
||||
|
||||
impl SystemTime {
|
||||
pub const MAX: SystemTime = SystemTime::from_intervals(i64::MAX);
|
||||
pub const MIN: SystemTime = SystemTime::from_intervals(0);
|
||||
|
||||
pub fn now() -> SystemTime {
|
||||
unsafe {
|
||||
let mut t: SystemTime = mem::zeroed();
|
||||
c::GetSystemTimePreciseAsFileTime(&mut t.t);
|
||||
t
|
||||
}
|
||||
}
|
||||
|
||||
const fn from_intervals(intervals: i64) -> SystemTime {
|
||||
SystemTime {
|
||||
t: c::FILETIME {
|
||||
dwLowDateTime: intervals as u32,
|
||||
dwHighDateTime: (intervals >> 32) as u32,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn intervals(&self) -> i64 {
|
||||
(self.t.dwLowDateTime as i64) | ((self.t.dwHighDateTime as i64) << 32)
|
||||
}
|
||||
|
||||
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
|
||||
let me = self.intervals();
|
||||
let other = other.intervals();
|
||||
if me >= other {
|
||||
Ok(intervals2dur((me - other) as u64))
|
||||
} else {
|
||||
Err(intervals2dur((other - me) as u64))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
let intervals = self.intervals().checked_add(checked_dur2intervals(other)?)?;
|
||||
Some(SystemTime::from_intervals(intervals))
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
// Windows does not support times before 1601, hence why we don't
|
||||
// support negatives. In order to tackle this, we try to convert the
|
||||
// resulting value into an u64, which should obviously fail in the case
|
||||
// that the value is below zero.
|
||||
let intervals: u64 =
|
||||
self.intervals().checked_sub(checked_dur2intervals(other)?)?.try_into().ok()?;
|
||||
Some(SystemTime::from_intervals(intervals as i64))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for SystemTime {
|
||||
fn eq(&self, other: &SystemTime) -> bool {
|
||||
self.intervals() == other.intervals()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for SystemTime {}
|
||||
|
||||
impl PartialOrd for SystemTime {
|
||||
fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for SystemTime {
|
||||
fn cmp(&self, other: &SystemTime) -> Ordering {
|
||||
self.intervals().cmp(&other.intervals())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SystemTime {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("SystemTime").field("intervals", &self.intervals()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<c::FILETIME> for SystemTime {
|
||||
fn from(t: c::FILETIME) -> SystemTime {
|
||||
SystemTime { t }
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoInner<c::FILETIME> for SystemTime {
|
||||
fn into_inner(self) -> c::FILETIME {
|
||||
self.t
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for SystemTime {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.intervals().hash(state)
|
||||
}
|
||||
}
|
||||
|
|
@ -275,26 +275,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
interp_ok(Scalar::from_i32(-1)) // Return non-zero on success
|
||||
}
|
||||
|
||||
#[allow(non_snake_case, clippy::arithmetic_side_effects)]
|
||||
#[allow(clippy::arithmetic_side_effects)]
|
||||
fn system_time_since_windows_epoch(&self, time: &SystemTime) -> InterpResult<'tcx, Duration> {
|
||||
let this = self.eval_context_ref();
|
||||
|
||||
let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC");
|
||||
let INTERVALS_TO_UNIX_EPOCH = this.eval_windows_u64("time", "INTERVALS_TO_UNIX_EPOCH");
|
||||
let SECONDS_TO_UNIX_EPOCH = INTERVALS_TO_UNIX_EPOCH / INTERVALS_PER_SEC;
|
||||
// The amount of seconds between 1601/1/1 and 1970/1/1.
|
||||
// See https://learn.microsoft.com/en-us/windows/win32/sysinfo/converting-a-time-t-value-to-a-file-time
|
||||
// (just divide by the number of 100 ns intervals per second).
|
||||
const SECONDS_TO_UNIX_EPOCH: u64 = 11_644_473_600;
|
||||
|
||||
interp_ok(system_time_to_duration(time)? + Duration::from_secs(SECONDS_TO_UNIX_EPOCH))
|
||||
}
|
||||
|
||||
#[allow(non_snake_case, clippy::arithmetic_side_effects)]
|
||||
fn windows_ticks_for(&self, duration: Duration) -> InterpResult<'tcx, u64> {
|
||||
let this = self.eval_context_ref();
|
||||
// 1 interval = 100 ns.
|
||||
// See https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime
|
||||
const NANOS_PER_INTERVAL: u128 = 100;
|
||||
|
||||
let NANOS_PER_SEC = this.eval_windows_u64("time", "NANOS_PER_SEC");
|
||||
let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC");
|
||||
let NANOS_PER_INTERVAL = NANOS_PER_SEC / INTERVALS_PER_SEC;
|
||||
|
||||
let ticks = u64::try_from(duration.as_nanos() / u128::from(NANOS_PER_INTERVAL))
|
||||
let ticks = u64::try_from(duration.as_nanos() / NANOS_PER_INTERVAL)
|
||||
.map_err(|_| err_unsup_format!("programs running more than 2^64 Windows ticks after the Windows epoch are not supported"))?;
|
||||
interp_ok(ticks)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue