Skip to content
Open
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
1 change: 1 addition & 0 deletions src/clocks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
8 changes: 8 additions & 0 deletions src/clocks/monotonic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
39 changes: 39 additions & 0 deletions src/clocks/monotonic/unix.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::mem;
use std::time::Duration;

#[derive(Clone, Copy, Debug, Default)]
pub struct Monotonic {
_default: (),
Expand Down Expand Up @@ -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
}
17 changes: 16 additions & 1 deletion src/clocks/monotonic/wasm_browser.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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()
}
16 changes: 16 additions & 0 deletions src/clocks/monotonic/wasm_wasi.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::mem;
use std::time::Duration;

#[derive(Clone, Copy, Debug, Default)]
pub struct Monotonic {
_default: (),
Expand All @@ -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()
}
16 changes: 16 additions & 0 deletions src/clocks/monotonic/windows.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::mem;
use std::time::Duration;
use winapi::um::profileapi;

#[derive(Clone, Copy, Debug)]
Expand Down Expand Up @@ -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()
}
4 changes: 2 additions & 2 deletions src/detection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
60 changes: 54 additions & 6 deletions src/instant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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<Instant>) -> 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
Expand All @@ -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<Duration> {
self.0.checked_sub(earlier.0).map(Duration::from_nanos)
pub fn checked_duration_since(&self, earlier: impl Into<Instant>) -> Option<Duration> {
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
Expand All @@ -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<Instant>) -> Duration {
self.checked_duration_since(earlier.into()).unwrap_or_default()
}

/// Returns the amount of time elapsed since this instant was created.
Expand Down Expand Up @@ -277,6 +278,18 @@ impl Into<prost_types::Timestamp> for Instant {
}
}

impl From<Instant> for std::time::Instant {
fn from(val: Instant) -> Self {
to_std_instant(val.0)
}
}

impl From<std::time::Instant> for Instant {
fn from(val: std::time::Instant) -> Self {
Instant(from_std_instant(val))
}
}

#[cfg(test)]
mod tests {
use once_cell::sync::Lazy;
Expand All @@ -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();
Expand Down Expand Up @@ -390,6 +405,8 @@ mod tests {
dur
}

let _guard = RECENT_LOCK.lock().unwrap();

let dur = nanos_to_dur(1 << 64);
let now = Instant::now();

Expand All @@ -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);
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ static GLOBAL_CALIBRATION: OnceCell<Calibration> = OnceCell::new();

// Per-thread clock override, used by `quanta::with_clock`, `Instant::now`, and sometimes `Instant::recent`.
thread_local! {
static CLOCK_OVERRIDE: RefCell<Option<Clock>> = RefCell::new(None);
static CLOCK_OVERRIDE: RefCell<Option<Clock>> = const { RefCell::new(None) };
}

// Run 500 rounds of calibration before we start actually seeing what the numbers look like.
Expand Down