webrender_api/
precise_time_ns.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Copyright 2024 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

pub fn precise_time_ns() -> u64 {
    platform::now()
}

#[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))]
mod platform {
    use libc::timespec;

    #[allow(unsafe_code)]
    pub(super) fn now() -> u64 {
        // SAFETY: libc::timespec is zero initializable.
        let time = unsafe {
            let mut time: timespec = std::mem::zeroed();
            libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut time);
            time
        };
        (time.tv_sec as u64) * 1000000000 + (time.tv_nsec as u64)
    }
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
mod platform {
    use std::sync::LazyLock;

    use mach2::mach_time::{mach_absolute_time, mach_timebase_info};

    #[allow(unsafe_code)]
    fn timebase_info() -> &'static mach_timebase_info {
        static TIMEBASE_INFO: LazyLock<mach_timebase_info> = LazyLock::new(|| {
            let mut timebase_info = mach_timebase_info { numer: 0, denom: 0 };
            unsafe { mach_timebase_info(&mut timebase_info) };
            timebase_info
        });
        &TIMEBASE_INFO
    }

    #[allow(unsafe_code)]
    pub(super) fn now() -> u64 {
        let timebase_info = timebase_info();
        let absolute_time = unsafe { mach_absolute_time() };
        absolute_time * timebase_info.numer as u64 / timebase_info.denom as u64
    }
}

#[cfg(target_os = "windows")]
mod platform {
    use std::sync::atomic::{AtomicU64, Ordering};

    use windows_sys::Win32::System::Performance::{
        QueryPerformanceCounter, QueryPerformanceFrequency,
    };

    /// The frequency of the value returned by `QueryPerformanceCounter` in counts per
    /// second. This is taken from the Rust source code at:
    /// <https://github.com/rust-lang/rust/blob/1a1cc050d8efc906ede39f444936ade1fdc9c6cb/library/std/src/sys/pal/windows/time.rs#L197>
    #[allow(unsafe_code)]
    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: AtomicU64 = 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;
        let result = unsafe { QueryPerformanceFrequency(&mut frequency) };

        if result == 0 {
            return 0;
        }

        FREQUENCY.store(frequency as u64, Ordering::Relaxed);
        frequency
    }

    #[allow(unsafe_code)]
    /// Get the current instant value in nanoseconds.
    /// Originally from: <https://github.com/rust-lang/rust/blob/1a1cc050d8efc906ede39f444936ade1fdc9c6cb/library/std/src/sys/pal/windows/time.rs#L175>
    pub(super) fn now() -> u64 {
        let mut counter_value = 0;
        unsafe { QueryPerformanceCounter(&mut counter_value) };

        /// Computes (value*numer)/denom without overflow, as long as both
        /// (numer*denom) and the overall result fit into i64 (which is the case
        /// for our time conversions).
        /// Originally from: <https://github.com/rust-lang/rust/blob/1a1cc050d8efc906ede39f444936ade1fdc9c6cb/library/std/src/sys_common/mod.rs#L75>
        fn mul_div_u64(value: u64, numer: u64, denom: u64) -> u64 {
            let q = value / denom;
            let r = value % denom;
            // Decompose value as (value/denom*denom + value%denom),
            // substitute into (value*numer)/denom and simplify.
            // r < denom, so (denom*numer) is the upper bound of (r*numer)
            q * numer + r * numer / denom
        }

        static NANOSECONDS_PER_SECOND: u64 = 1_000_000_000;
        mul_div_u64(
            counter_value as u64,
            NANOSECONDS_PER_SECOND,
            frequency() as u64,
        )
    }
}