Skip to main content

fast_clock/
tsc.rs

1use crate::{
2    Clock,
3    wrapping_u64::{U64Calibration, WrappingU64Instant, WrappingU64Time},
4};
5
6/// The x86_64 timestamp counter (TSC).
7///
8/// Note that not all TSC implementations have a constant frequency.
9/// On Linux, [`try_new_linux_sys`](Self::try_new_linux_sys) checks that the frequency is constant.
10#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
11pub struct Tsc(());
12
13impl Clock for Tsc {
14    type Time = WrappingU64Time;
15    type Calibration = U64Calibration;
16
17    #[inline(always)]
18    fn now(&self) -> WrappingU64Instant {
19        WrappingU64Instant(unsafe { core::arch::x86_64::_rdtsc() })
20    }
21}
22
23/// Error returned when a stable TSC is not available on the current system.
24#[derive(Debug)]
25#[non_exhaustive]
26pub struct TscUnavailable;
27
28impl core::fmt::Display for TscUnavailable {
29    fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
30        formatter.write_str("No stable TSC available")
31    }
32}
33
34#[cfg(feature = "std")]
35impl std::error::Error for TscUnavailable {}
36
37impl Tsc {
38    /// Returns `Ok(Tsc)` if the CPUID TSC flag is set.
39    ///
40    /// The TSC flag indicates the counter exists but does not guarantee stability
41    /// across cores or CPU power states. Prefer [`Tsc::try_new_linux_sys`] on Linux.
42    pub fn try_new_assume_stable() -> Result<Self, TscUnavailable> {
43        let edx = core::arch::x86_64::__cpuid(1).edx;
44        if (edx & (1 << 4)) != 0 {
45            Ok(Tsc(()))
46        } else {
47            Err(TscUnavailable)
48        }
49    }
50
51    /// Returns `Ok(Tsc)` if the Linux kernel reports `tsc` as an available clocksource.
52    ///
53    /// A TSC listed as an available clocksource means the kernel has verified stability
54    /// across cores and power state changes, making it safe for benchmarking.
55    #[cfg(all(target_os = "linux", feature = "std"))]
56    pub fn try_new_linux_sys() -> Result<Self, TscUnavailable> {
57        let stable_tsc_detected = std::fs::read_to_string(
58            "/sys/devices/system/clocksource/clocksource0/available_clocksource",
59        )
60        .is_ok_and(|x| x.contains("tsc"));
61        if stable_tsc_detected {
62            Ok(Tsc(()))
63        } else {
64            Err(TscUnavailable)
65        }
66    }
67}