1mod default;
7mod instant;
8
9use std::sync::atomic::{self, AtomicBool};
10
11pub use instant::Instant;
12pub use std::time::{Duration, SystemTime};
13
14type U64Microsecond = u64;
16
17static mut UPTIME_SOURCE: fn() -> U64Microsecond = default::default_uptime_source;
18static mut UPTIME_PAUSE: fn(bool) = default::default_uptime_pause;
19static mut SYSTEM_TIME_VALID: fn() -> bool = || true;
20static mut IMPLEMENTATION_NAME: &str = default::DEFAULT_IMPL_NAME;
21
22static IS_PAUSED: AtomicBool = AtomicBool::new(false);
23static PAUSE_IMPLEMENTED: AtomicBool = AtomicBool::new(true);
24static TIME_IMPL_FROZEN: atomic::AtomicBool = atomic::AtomicBool::new(false);
25
26#[inline]
30pub fn uptime() -> Duration {
31 TIME_IMPL_FROZEN.store(true, atomic::Ordering::Relaxed);
32 Duration::from_micros(unsafe { UPTIME_SOURCE() })
33}
34
35#[allow(missing_docs)]
36#[derive(Debug, thiserror::Error, Clone, Copy)]
37pub enum PauseError {
38 #[error("Pause not implemented for {} time source", unsafe { IMPLEMENTATION_NAME })]
39 PauseNotImplemented,
40 #[error("Cannot pause if already paused")]
41 AlreadyPaused,
42 #[error("Cannot un-pause if not paused")]
43 NotPaused,
44}
45
46#[inline]
53pub fn try_pause(should_pause: bool) -> Result<(), PauseError> {
54 if IS_PAUSED.swap(should_pause, atomic::Ordering::Relaxed) == should_pause {
55 if should_pause {
56 Err(PauseError::AlreadyPaused)
57 } else {
58 Err(PauseError::NotPaused)
59 }
60 } else if !PAUSE_IMPLEMENTED.load(atomic::Ordering::Relaxed) {
61 Err(PauseError::PauseNotImplemented)
62 } else {
63 unsafe { UPTIME_PAUSE(should_pause) };
64 Ok(())
65 }
66}
67
68#[inline]
70pub fn is_paused() -> bool {
71 IS_PAUSED.load(atomic::Ordering::Relaxed)
72}
73
74#[inline]
76pub fn pause_implemented() -> bool {
77 PAUSE_IMPLEMENTED.load(atomic::Ordering::Relaxed)
78}
79
80#[inline]
83pub fn system_time() -> Option<SystemTime> {
84 TIME_IMPL_FROZEN.store(true, atomic::Ordering::Relaxed);
85 if unsafe { SYSTEM_TIME_VALID() } {
86 Some(SystemTime::now())
87 } else {
88 None
89 }
90}
91
92#[doc(hidden)]
93pub mod __private {
94
95 #[derive(Debug, Clone, Copy)]
96 pub struct TimeImplementation {
97 pub implementation_name: &'static str,
98 pub uptime: fn() -> super::U64Microsecond,
100 pub pause: Option<fn(bool) -> ()>,
103 pub system_time_valid: fn() -> bool,
105 }
106
107 pub unsafe fn set_time_implementation(time_imp: TimeImplementation) {
116 use std::sync::atomic::Ordering;
117 assert!(
118 !super::TIME_IMPL_FROZEN.swap(true, Ordering::SeqCst),
119 "Cannot set time source after it has been used or previously set(old: {}, new: {})",
120 super::IMPLEMENTATION_NAME,
121 time_imp.implementation_name
122 );
123 super::UPTIME_SOURCE = time_imp.uptime;
124 super::IMPLEMENTATION_NAME = time_imp.implementation_name;
125 super::SYSTEM_TIME_VALID = time_imp.system_time_valid;
126 if let Some(pause) = time_imp.pause {
127 super::PAUSE_IMPLEMENTED.store(true, Ordering::SeqCst);
128 super::UPTIME_PAUSE = pause;
129 } else {
130 super::PAUSE_IMPLEMENTED.store(false, Ordering::SeqCst);
131 super::UPTIME_PAUSE = |_| {};
132 }
133 }
134}
135
136#[cfg(test)]
137mod test {
138 use std::thread;
139
140 fn test_time() {
141 use super::*;
142 let start = uptime().as_micros();
143 thread::sleep(Duration::from_millis(100));
144 let end = uptime().as_micros();
145 assert!(end.saturating_sub(start) >= 100_000);
146 }
147
148 fn test_pause() {
149 use super::*;
150 try_pause(true).expect("Pause Error");
151 let start = uptime().as_micros();
152 thread::sleep(Duration::from_millis(1000));
153 let end = uptime().as_micros();
154 assert!(end.saturating_sub(start) < 100);
156 try_pause(false).expect("Pause Error");
157 thread::sleep(Duration::from_millis(1000));
158 let end = uptime().as_micros();
159 assert!(end.saturating_sub(start) >= 1_000_000);
160 }
161
162 #[test]
165 fn test_all() {
166 test_time();
167 test_pause();
168 }
169}