| 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) }; |
| } |
| } |