Skip to main content

win_time_sys/
performance.rs

1// Copyright (c) 2026 Mike Grier
2//! The performance counter, and the clock that reads it ([WT-D-3], [WT-D-4]).
3//!
4//! QPC's raw ticks, not `std::time::Instant` -- which reads the same counter but keeps its value to
5//! itself -- so a point can be recorded and read back. The counter is steady within a boot and, as
6//! Microsoft documents it, "independent of and isn't synchronized to any external time reference":
7//! its zero is unspecified, and a point means nothing after a restart.
8//!
9//! The tick's length is the counter's frequency, known only at run time. Microsoft documents it as
10//! "fixed at system boot and is consistent across all processors", so it is read once and kept.
11//!
12//! [WT-D-3]: https://github.com/MikeGrier/windows-threadpool-sys/blob/main/crates/win-time-sys/DESIGN-NOTES.md#wt-d-3
13//! [WT-D-4]: https://github.com/MikeGrier/windows-threadpool-sys/blob/main/crates/win-time-sys/DESIGN-NOTES.md#wt-d-4
14
15use core::num::NonZeroU64;
16use std::sync::OnceLock;
17
18use windows_sys::Win32::System::Performance::{QueryPerformanceCounter, QueryPerformanceFrequency};
19
20use crate::clock::{Clock, Steady};
21use crate::timeline::{TimePoint, Timeline};
22
23#[cfg(test)]
24mod tests;
25
26/// The performance counter: ticks at its frequency, from an unspecified zero within this boot.
27///
28/// Its points do not compare with interrupt time's, though both count from around the boot:
29///
30/// ```compile_fail,E0308
31/// use win_time_sys::{Clock, InterruptClock, PerformanceClock};
32///
33/// let _ = PerformanceClock.now() < InterruptClock.now();
34/// ```
35pub enum PerformanceCounter {}
36
37impl Timeline for PerformanceCounter {
38    fn ticks_per_second() -> NonZeroU64 {
39        static FREQUENCY: OnceLock<NonZeroU64> = OnceLock::new();
40        *FREQUENCY.get_or_init(frequency)
41    }
42}
43
44/// [`PerformanceCounter`], through `QueryPerformanceCounter`.
45#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
46pub struct PerformanceClock;
47
48impl Clock for PerformanceClock {
49    type Timeline = PerformanceCounter;
50
51    fn now(&self) -> TimePoint<PerformanceCounter> {
52        let mut ticks = 0;
53        // SAFETY: the out-parameter is a live, writable local for the duration of the call.
54        let succeeded = unsafe { QueryPerformanceCounter(&mut ticks) };
55        // Documented to "always succeed" on Windows XP and later, with a count that is never
56        // negative.
57        debug_assert_ne!(succeeded, 0, "QueryPerformanceCounter failed");
58        TimePoint::from_ticks(ticks.cast_unsigned())
59    }
60}
61
62// The counter is steady within a boot and never set.
63impl Steady for PerformanceClock {}
64
65/// The counter's frequency, in ticks per second.
66fn frequency() -> NonZeroU64 {
67    let mut per_second = 0;
68    // SAFETY: the out-parameter is a live, writable local for the duration of the call.
69    let succeeded = unsafe { QueryPerformanceFrequency(&mut per_second) };
70    // Documented to "always succeed" on Windows XP and later, so the frequency is never zero.
71    assert_ne!(succeeded, 0, "QueryPerformanceFrequency failed");
72    NonZeroU64::new(per_second.cast_unsigned()).expect("a performance counter frequency of zero")
73}