webrender_api/
precise_time_ns.rs

1// Copyright 2024 The Servo Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11pub fn precise_time_ns() -> u64 {
12    platform::now()
13}
14
15#[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))]
16mod platform {
17    use libc::timespec;
18
19    #[allow(unsafe_code)]
20    pub(super) fn now() -> u64 {
21        // SAFETY: libc::timespec is zero initializable.
22        let time = unsafe {
23            let mut time: timespec = std::mem::zeroed();
24            libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut time);
25            time
26        };
27        (time.tv_sec as u64) * 1000000000 + (time.tv_nsec as u64)
28    }
29}
30
31#[cfg(any(target_os = "macos", target_os = "ios"))]
32mod platform {
33    use std::sync::LazyLock;
34
35    use mach2::mach_time::{mach_absolute_time, mach_timebase_info};
36
37    #[allow(unsafe_code)]
38    fn timebase_info() -> &'static mach_timebase_info {
39        static TIMEBASE_INFO: LazyLock<mach_timebase_info> = LazyLock::new(|| {
40            let mut timebase_info = mach_timebase_info { numer: 0, denom: 0 };
41            unsafe { mach_timebase_info(&mut timebase_info) };
42            timebase_info
43        });
44        &TIMEBASE_INFO
45    }
46
47    #[allow(unsafe_code)]
48    pub(super) fn now() -> u64 {
49        let timebase_info = timebase_info();
50        let absolute_time = unsafe { mach_absolute_time() };
51        absolute_time * timebase_info.numer as u64 / timebase_info.denom as u64
52    }
53}
54
55#[cfg(target_os = "windows")]
56mod platform {
57    use std::sync::atomic::{AtomicU64, Ordering};
58
59    use windows_sys::Win32::System::Performance::{
60        QueryPerformanceCounter, QueryPerformanceFrequency,
61    };
62
63    /// The frequency of the value returned by `QueryPerformanceCounter` in counts per
64    /// second. This is taken from the Rust source code at:
65    /// <https://github.com/rust-lang/rust/blob/1a1cc050d8efc906ede39f444936ade1fdc9c6cb/library/std/src/sys/pal/windows/time.rs#L197>
66    #[allow(unsafe_code)]
67    fn frequency() -> i64 {
68        // Either the cached result of `QueryPerformanceFrequency` or `0` for
69        // uninitialized. Storing this as a single `AtomicU64` allows us to use
70        // `Relaxed` operations, as we are only interested in the effects on a
71        // single memory location.
72        static FREQUENCY: AtomicU64 = AtomicU64::new(0);
73
74        let cached = FREQUENCY.load(Ordering::Relaxed);
75        // If a previous thread has filled in this global state, use that.
76        if cached != 0 {
77            return cached as i64;
78        }
79        // ... otherwise learn for ourselves ...
80        let mut frequency = 0;
81        let result = unsafe { QueryPerformanceFrequency(&mut frequency) };
82
83        if result == 0 {
84            return 0;
85        }
86
87        FREQUENCY.store(frequency as u64, Ordering::Relaxed);
88        frequency
89    }
90
91    #[allow(unsafe_code)]
92    /// Get the current instant value in nanoseconds.
93    /// Originally from: <https://github.com/rust-lang/rust/blob/1a1cc050d8efc906ede39f444936ade1fdc9c6cb/library/std/src/sys/pal/windows/time.rs#L175>
94    pub(super) fn now() -> u64 {
95        let mut counter_value = 0;
96        unsafe { QueryPerformanceCounter(&mut counter_value) };
97
98        /// Computes (value*numer)/denom without overflow, as long as both
99        /// (numer*denom) and the overall result fit into i64 (which is the case
100        /// for our time conversions).
101        /// Originally from: <https://github.com/rust-lang/rust/blob/1a1cc050d8efc906ede39f444936ade1fdc9c6cb/library/std/src/sys_common/mod.rs#L75>
102        fn mul_div_u64(value: u64, numer: u64, denom: u64) -> u64 {
103            let q = value / denom;
104            let r = value % denom;
105            // Decompose value as (value/denom*denom + value%denom),
106            // substitute into (value*numer)/denom and simplify.
107            // r < denom, so (denom*numer) is the upper bound of (r*numer)
108            q * numer + r * numer / denom
109        }
110
111        static NANOSECONDS_PER_SECOND: u64 = 1_000_000_000;
112        mul_div_u64(
113            counter_value as u64,
114            NANOSECONDS_PER_SECOND,
115            frequency() as u64,
116        )
117    }
118}
119