diff --git a/src/clocks/mod.rs b/src/clocks/mod.rs index 5696393..2bc1186 100644 --- a/src/clocks/mod.rs +++ b/src/clocks/mod.rs @@ -2,4 +2,5 @@ mod counter; pub use self::counter::Counter; mod monotonic; +pub(crate) use self::monotonic::{to_std_instant, from_std_instant}; pub use self::monotonic::Monotonic; diff --git a/src/clocks/monotonic/mod.rs b/src/clocks/monotonic/mod.rs index 80cbf89..1ca8479 100644 --- a/src/clocks/monotonic/mod.rs +++ b/src/clocks/monotonic/mod.rs @@ -2,18 +2,26 @@ mod windows; #[cfg(target_os = "windows")] pub use self::windows::Monotonic; +#[cfg(target_os = "windows")] +pub(crate) use self::windows::{to_std_instant, from_std_instant}; #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] mod wasm_browser; #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] pub use self::wasm_browser::Monotonic; +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +pub(crate) use self::wasm_browser::{to_std_instant, from_std_instant}; #[cfg(all(target_arch = "wasm32", target_os = "wasi"))] mod wasm_wasi; #[cfg(all(target_arch = "wasm32", target_os = "wasi"))] pub use self::wasm_wasi::Monotonic; +#[cfg(all(target_arch = "wasm32", target_os = "wasi"))] +pub(crate) use self::wasm_wasi::{to_std_instant, from_std_instant}; #[cfg(not(any(target_os = "windows", target_arch = "wasm32")))] mod unix; #[cfg(not(any(target_os = "windows", target_arch = "wasm32")))] pub use self::unix::Monotonic; +#[cfg(not(any(target_os = "windows", target_arch = "wasm32")))] +pub(crate) use self::unix::{to_std_instant, from_std_instant}; diff --git a/src/clocks/monotonic/unix.rs b/src/clocks/monotonic/unix.rs index bb73e2b..8496e2f 100644 --- a/src/clocks/monotonic/unix.rs +++ b/src/clocks/monotonic/unix.rs @@ -1,3 +1,6 @@ +use std::mem; +use std::time::Duration; + #[derive(Clone, Copy, Debug, Default)] pub struct Monotonic { _default: (), @@ -36,3 +39,39 @@ impl Monotonic { ts.tv_sec as u64 * 1_000_000_000 + ts.tv_nsec as u64 } } + +// std::time::Instant is represented as +// struct Nanoseconds(u32); +// +// struct Timespec { +// tv_sec: i64, +// tv_nsec: Nanoseconds, +// } + +// struct Instant { +// t: Timespec, +// } + +struct Nanoseconds(u32); + +struct Timespec { + tv_sec: i64, + tv_nsec: Nanoseconds, +} + +pub(crate) fn to_std_instant(instant: u64) -> std::time::Instant { + let dur = Duration::from_nanos(instant); + + unsafe { + mem::transmute(Timespec { + tv_sec: dur.as_secs() as i64, + tv_nsec: Nanoseconds(dur.subsec_nanos()), + }) + } +} + +pub(crate) fn from_std_instant(instant: std::time::Instant) -> u64 { + let ts: Timespec = unsafe { mem::transmute(instant) }; + + ts.tv_sec as u64 * 1_000_000_000 + ts.tv_nsec.0 as u64 +} \ No newline at end of file diff --git a/src/clocks/monotonic/wasm_browser.rs b/src/clocks/monotonic/wasm_browser.rs index b3f94a9..efc564e 100644 --- a/src/clocks/monotonic/wasm_browser.rs +++ b/src/clocks/monotonic/wasm_browser.rs @@ -1,5 +1,6 @@ use std::cell::OnceCell; - +use std::mem; +use std::time::Duration; use web_sys::{ js_sys::Reflect, wasm_bindgen::{JsCast, JsValue}, @@ -37,3 +38,17 @@ impl Monotonic { f64::trunc(now * 1_000_000.0) as u64 } } + + +// std::time::Instant is represented as +// struct Instant(std::time::Duration); + +pub(crate) fn to_std_instant(instant: u64) -> std::time::Instant { + unsafe { mem::transmute(Duration::from_nanos(instant)) } +} + +pub(crate) fn from_std_instant(instant: std::time::Instant) -> u64 { + let dur: Duration = unsafe { mem::transmute(instant) }; + + dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() +} \ No newline at end of file diff --git a/src/clocks/monotonic/wasm_wasi.rs b/src/clocks/monotonic/wasm_wasi.rs index f230f68..329a98d 100644 --- a/src/clocks/monotonic/wasm_wasi.rs +++ b/src/clocks/monotonic/wasm_wasi.rs @@ -1,3 +1,6 @@ +use std::mem; +use std::time::Duration; + #[derive(Clone, Copy, Debug, Default)] pub struct Monotonic { _default: (), @@ -8,3 +11,16 @@ impl Monotonic { unsafe { wasi::clock_time_get(wasi::CLOCKID_MONOTONIC, 1).expect("failed to get time") } } } + +// std::time::Instant is represented as +// struct Instant(std::time::Duration); + +pub(crate) fn to_std_instant(instant: u64) -> std::time::Instant { + unsafe { mem::transmute(Duration::from_nanos(instant)) } +} + +pub(crate) fn from_std_instant(instant: std::time::Instant) -> u64 { + let dur: Duration = unsafe { mem::transmute(instant) }; + + dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() +} \ No newline at end of file diff --git a/src/clocks/monotonic/windows.rs b/src/clocks/monotonic/windows.rs index ee48924..861b101 100644 --- a/src/clocks/monotonic/windows.rs +++ b/src/clocks/monotonic/windows.rs @@ -1,4 +1,5 @@ use std::mem; +use std::time::Duration; use winapi::um::profileapi; #[derive(Clone, Copy, Debug)] @@ -40,3 +41,18 @@ impl Default for Monotonic { } } } + +// std::time::Instant is represented as +// struct Instant { +// t: Duration, +// } + +pub(crate) fn to_std_instant(instant: u64) -> std::time::Instant { + unsafe { mem::transmute(Duration::from_nanos(instant)) } +} + +pub(crate) fn from_std_instant(instant: std::time::Instant) -> u64 { + let dur: Duration = unsafe { mem::transmute(instant) }; + + dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() +} \ No newline at end of file diff --git a/src/detection.rs b/src/detection.rs index 84f06e9..efea0dc 100644 --- a/src/detection.rs +++ b/src/detection.rs @@ -4,10 +4,10 @@ pub fn has_counter_support() -> bool { let cpuid = raw_cpuid::CpuId::new(); let has_invariant_tsc = cpuid .get_advanced_power_mgmt_info() - .map_or(false, |apm| apm.has_invariant_tsc()); + .is_some_and(|apm| apm.has_invariant_tsc()); let has_rdtscp = cpuid .get_extended_processor_and_feature_identifiers() - .map_or(false, |epf| epf.has_rdtscp()); + .is_some_and(|epf| epf.has_rdtscp()); has_invariant_tsc && has_rdtscp } diff --git a/src/instant.rs b/src/instant.rs index ee31345..f8cbde5 100644 --- a/src/instant.rs +++ b/src/instant.rs @@ -2,6 +2,7 @@ use std::cmp::{Ord, Ordering, PartialOrd}; use std::fmt; use std::ops::{Add, AddAssign, Sub, SubAssign}; use std::time::Duration; +use crate::clocks::{from_std_instant, to_std_instant}; /// A point-in-time wall-clock measurement. /// @@ -84,8 +85,8 @@ impl Instant { /// let new_now = clock.now(); /// println!("{:?}", new_now.duration_since(now)); /// ``` - pub fn duration_since(&self, earlier: Instant) -> Duration { - self.checked_duration_since(earlier).unwrap_or_default() + pub fn duration_since(&self, earlier: impl Into) -> Duration { + self.checked_duration_since(earlier.into()).unwrap_or_default() } /// Returns the amount of time elapsed from another instant to this one, or `None` if that @@ -110,8 +111,8 @@ impl Instant { /// println!("{:?}", new_now.checked_duration_since(now)); /// println!("{:?}", now.checked_duration_since(new_now)); // None /// ``` - pub fn checked_duration_since(&self, earlier: Instant) -> Option { - self.0.checked_sub(earlier.0).map(Duration::from_nanos) + pub fn checked_duration_since(&self, earlier: impl Into) -> Option { + self.0.checked_sub(earlier.into().0).map(Duration::from_nanos) } /// Returns the amount of time elapsed from another instant to this one, or zero duration if @@ -136,8 +137,8 @@ impl Instant { /// println!("{:?}", new_now.saturating_duration_since(now)); /// println!("{:?}", now.saturating_duration_since(new_now)); // 0ns /// ``` - pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { - self.checked_duration_since(earlier).unwrap_or_default() + pub fn saturating_duration_since(&self, earlier: impl Into) -> Duration { + self.checked_duration_since(earlier.into()).unwrap_or_default() } /// Returns the amount of time elapsed since this instant was created. @@ -277,6 +278,18 @@ impl Into for Instant { } } +impl From for std::time::Instant { + fn from(val: Instant) -> Self { + to_std_instant(val.0) + } +} + +impl From for Instant { + fn from(val: std::time::Instant) -> Self { + Instant(from_std_instant(val)) + } +} + #[cfg(test)] mod tests { use once_cell::sync::Lazy; @@ -294,6 +307,8 @@ mod tests { ignore = "WASM thread cannot sleep" )] fn test_now() { + let _guard = RECENT_LOCK.lock().unwrap(); + let t0 = Instant::now(); thread::sleep(Duration::from_millis(15)); let t1 = Instant::now(); @@ -390,6 +405,8 @@ mod tests { dur } + let _guard = RECENT_LOCK.lock().unwrap(); + let dur = nanos_to_dur(1 << 64); let now = Instant::now(); @@ -400,4 +417,35 @@ mod tests { assert_ne!(Some(now), behind); assert_ne!(Some(now), ahead); } + + #[test] + fn test_into_std_instant() { + let _guard = RECENT_LOCK.lock().unwrap(); + + let instant = Instant::now(); + let std_instant: std::time::Instant = instant.into(); + let instant_from_std: Instant = std_instant.into(); + + assert_eq!(instant, instant_from_std); + + thread::sleep(Duration::from_millis(1)); + + let now = Instant::now(); + + assert_eq!( + now.duration_since(instant), + now.duration_since(std_instant) + ); + } + + #[test] + fn test_from_std_instant() { + let _guard = RECENT_LOCK.lock().unwrap(); + + let std_instant: std::time::Instant = std::time::Instant::now(); + let instant: Instant = std_instant.into(); + let std_instant_from_instant: std::time::Instant = instant.into(); + + assert_eq!(std_instant, std_instant_from_instant); + } } diff --git a/src/lib.rs b/src/lib.rs index 24b699e..88d5c27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -169,7 +169,7 @@ static GLOBAL_CALIBRATION: OnceCell = OnceCell::new(); // Per-thread clock override, used by `quanta::with_clock`, `Instant::now`, and sometimes `Instant::recent`. thread_local! { - static CLOCK_OVERRIDE: RefCell> = RefCell::new(None); + static CLOCK_OVERRIDE: RefCell> = const { RefCell::new(None) }; } // Run 500 rounds of calibration before we start actually seeing what the numbers look like.