1#![cfg_attr(docsrs, feature(doc_cfg))]
35
36mod instant;
37#[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))]
38mod tsc_now;
39
40#[cfg(all(feature = "atomic", target_has_atomic = "64"))]
41#[cfg_attr(docsrs, doc(cfg(all(feature = "atomic", target_has_atomic = "64"))))]
42pub use instant::Atomic;
43pub use instant::{Anchor, Instant};
44
45#[inline]
50pub fn is_tsc_available() -> bool {
51 #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))]
52 {
53 tsc_now::is_tsc_available()
54 }
55 #[cfg(not(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64"))))]
56 {
57 false
58 }
59}
60
61#[inline]
62pub(crate) fn current_cycle() -> u64 {
63 #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))]
64 {
65 if tsc_now::is_tsc_available() {
66 tsc_now::current_cycle()
67 } else {
68 current_cycle_fallback()
69 }
70 }
71 #[cfg(not(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64"))))]
72 {
73 current_cycle_fallback()
74 }
75}
76
77#[cfg(not(feature = "fallback-coarse"))]
78pub(crate) fn current_cycle_fallback() -> u64 {
79 web_time::SystemTime::now()
80 .duration_since(web_time::UNIX_EPOCH)
81 .map(|d| d.as_nanos() as u64)
82 .unwrap_or(0)
83}
84
85#[cfg(feature = "fallback-coarse")]
86pub(crate) fn current_cycle_fallback() -> u64 {
87 let coarse = coarsetime::Instant::now_without_cache_update();
88 coarsetime::Duration::from_ticks(coarse.as_ticks()).as_nanos()
89}
90
91#[inline]
92pub(crate) fn nanos_per_cycle() -> f64 {
93 #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))]
94 {
95 tsc_now::nanos_per_cycle()
96 }
97 #[cfg(not(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64"))))]
98 {
99 1.0
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use rand::Rng;
107 use std::time::{Duration, Instant as StdInstant};
108 use wasm_bindgen_test::wasm_bindgen_test;
109
110 #[test]
111 #[wasm_bindgen_test]
112 fn test_is_tsc_available() {
113 let _ = is_tsc_available();
114 }
115
116 #[test]
117 #[wasm_bindgen_test]
118 fn test_monotonic() {
119 let mut prev = 0;
120 for _ in 0..10000 {
121 let cur = current_cycle();
122 assert!(cur >= prev);
123 prev = cur;
124 }
125 }
126
127 #[test]
128 #[wasm_bindgen_test]
129 fn test_nanos_per_cycle() {
130 let _ = nanos_per_cycle();
131 }
132
133 #[test]
134 #[wasm_bindgen_test]
135 fn test_unix_time() {
136 let now = Instant::now();
137 let anchor = Anchor::new();
138 let unix_nanos = now.as_unix_nanos(&anchor);
139 assert!(unix_nanos > 0);
140 }
141
142 #[test]
143 fn test_duration() {
144 let mut rng = rand::thread_rng();
145 for _ in 0..10 {
146 let instant = Instant::now();
147 let std_instant = StdInstant::now();
148 std::thread::sleep(Duration::from_millis(rng.gen_range(100..500)));
149 let check = move || {
150 let duration_ns_minstant = instant.elapsed();
151 let duration_ns_std = std_instant.elapsed();
152
153 #[cfg(target_os = "windows")]
154 let expect_max_delta_ns = 40_000_000;
155 #[cfg(not(target_os = "windows"))]
156 let expect_max_delta_ns = 5_000_000;
157
158 let real_delta = (duration_ns_std.as_nanos() as i128
159 - duration_ns_minstant.as_nanos() as i128)
160 .abs();
161 assert!(
162 real_delta < expect_max_delta_ns,
163 "real delta: {}",
164 real_delta
165 );
166 };
167 check();
168 std::thread::spawn(check).join().expect("join failed");
169 }
170 }
171}