| use crate::ops::Neg; | |
| use crate::ptr::null; | |
| use crate::sys::pal::c; | |
| use crate::time::Duration; | |
| const NANOS_PER_SEC: u64 = 1_000_000_000; | |
| pub const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100; | |
| pub fn checked_dur2intervals(dur: &Duration) -> Option<i64> { | |
| dur.as_secs() | |
| .checked_mul(INTERVALS_PER_SEC)? | |
| .checked_add(dur.subsec_nanos() as u64 / 100)? | |
| .try_into() | |
| .ok() | |
| } | |
| pub fn intervals2dur(intervals: u64) -> Duration { | |
| Duration::new(intervals / INTERVALS_PER_SEC, ((intervals % INTERVALS_PER_SEC) * 100) as u32) | |
| } | |
| pub mod perf_counter { | |
| use super::NANOS_PER_SEC; | |
| use crate::sync::atomic::{Atomic, AtomicU64, Ordering}; | |
| use crate::sys::{c, cvt}; | |
| use crate::time::Duration; | |
| pub fn now() -> i64 { | |
| let mut qpc_value: i64 = 0; | |
| cvt(unsafe { c::QueryPerformanceCounter(&mut qpc_value) }).unwrap(); | |
| qpc_value | |
| } | |
| 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 | |
| // single memory location. | |
| static FREQUENCY: Atomic<u64> = AtomicU64::new(0); | |
| let cached = FREQUENCY.load(Ordering::Relaxed); | |
| // If a previous thread has filled in this global state, use that. | |
| if cached != 0 { | |
| return cached as i64; | |
| } | |
| // ... otherwise learn for ourselves ... | |
| let mut frequency = 0; | |
| unsafe { | |
| cvt(c::QueryPerformanceFrequency(&mut frequency)).unwrap(); | |
| } | |
| FREQUENCY.store(frequency as u64, Ordering::Relaxed); | |
| frequency | |
| } | |
| // 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) | |
| } | |
| } | |
| /// A timer you can wait on. | |
| 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, ()> { | |
| let handle = unsafe { | |
| c::CreateWaitableTimerExW( | |
| null(), | |
| null(), | |
| c::CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, | |
| c::TIMER_ALL_ACCESS, | |
| ) | |
| }; | |
| 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. | |
| // Therefore we negate the relative duration. | |
| let time = checked_dur2intervals(&duration).ok_or(())?.neg(); | |
| 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) }; | |
| } | |
| } |