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