1#[cfg(all(not(feature = "std"), target_has_atomic = "8"))]
2use core::cell::UnsafeCell;
3#[cfg(all(not(feature = "std"), target_has_atomic = "8"))]
4use core::sync::atomic::{AtomicU8, Ordering};
5
6use crate::{Instant, SystemTime};
7
8pub trait WallClock {
10 fn system_time(&self) -> Option<SystemTime>;
12}
13
14pub trait MonotonicClock {
16 fn instant(&self) -> Option<Instant>;
18}
19
20pub trait TimeContext: WallClock + MonotonicClock + Send + Sync {}
22
23impl<T> TimeContext for T where T: WallClock + MonotonicClock + Send + Sync {}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct GlobalTimeContextAlreadySet;
28
29#[cfg(feature = "std")]
30static GLOBAL_TIME_CONTEXT: std::sync::OnceLock<&'static dyn TimeContext> =
31 std::sync::OnceLock::new();
32
33#[cfg(all(not(feature = "std"), target_has_atomic = "8"))]
34struct NoStdGlobalTimeContext {
35 state: AtomicU8,
36 value: UnsafeCell<Option<&'static dyn TimeContext>>,
37}
38
39#[cfg(all(not(feature = "std"), target_has_atomic = "8"))]
40unsafe impl Sync for NoStdGlobalTimeContext {}
41
42#[cfg(all(not(feature = "std"), target_has_atomic = "8"))]
43impl NoStdGlobalTimeContext {
44 const UNINITIALIZED: u8 = 0;
45 const INITIALIZING: u8 = 1;
46 const READY: u8 = 2;
47
48 const fn new() -> Self {
49 Self {
50 state: AtomicU8::new(Self::UNINITIALIZED),
51 value: UnsafeCell::new(None),
52 }
53 }
54
55 fn set(&self, context: &'static dyn TimeContext) -> Result<(), GlobalTimeContextAlreadySet> {
56 match self.state.compare_exchange(
57 Self::UNINITIALIZED,
58 Self::INITIALIZING,
59 Ordering::AcqRel,
60 Ordering::Acquire,
61 ) {
62 Ok(_) => {
63 unsafe {
64 *self.value.get() = Some(context);
65 }
66 self.state.store(Self::READY, Ordering::Release);
67 Ok(())
68 }
69 Err(_) => {
70 while self.state.load(Ordering::Acquire) == Self::INITIALIZING {
71 core::hint::spin_loop();
72 }
73 Err(GlobalTimeContextAlreadySet)
74 }
75 }
76 }
77
78 fn get(&self) -> Option<&'static dyn TimeContext> {
79 let mut state = self.state.load(Ordering::Acquire);
80 while state == Self::INITIALIZING {
81 core::hint::spin_loop();
82 state = self.state.load(Ordering::Acquire);
83 }
84
85 if state == Self::READY {
86 unsafe { *self.value.get() }
87 } else {
88 None
89 }
90 }
91}
92
93#[cfg(all(not(feature = "std"), target_has_atomic = "8"))]
94static GLOBAL_TIME_CONTEXT: NoStdGlobalTimeContext = NoStdGlobalTimeContext::new();
95
96#[cfg(all(not(feature = "std"), not(target_has_atomic = "8")))]
97static mut GLOBAL_TIME_CONTEXT: Option<&'static dyn TimeContext> = None;
98
99pub fn set_global_time_context(
103 context: &'static dyn TimeContext,
104) -> Result<(), GlobalTimeContextAlreadySet> {
105 #[cfg(feature = "std")]
106 {
107 GLOBAL_TIME_CONTEXT
108 .set(context)
109 .map_err(|_| GlobalTimeContextAlreadySet)
110 }
111
112 #[cfg(all(not(feature = "std"), target_has_atomic = "8"))]
113 {
114 GLOBAL_TIME_CONTEXT.set(context)
115 }
116
117 #[cfg(all(not(feature = "std"), not(target_has_atomic = "8")))]
118 {
119 unsafe {
122 let current = core::ptr::read(core::ptr::addr_of!(GLOBAL_TIME_CONTEXT));
123 if current.is_some() {
124 Err(GlobalTimeContextAlreadySet)
125 } else {
126 core::ptr::write(core::ptr::addr_of_mut!(GLOBAL_TIME_CONTEXT), Some(context));
127 Ok(())
128 }
129 }
130 }
131}
132
133pub fn global_time_context() -> Option<&'static dyn TimeContext> {
135 #[cfg(feature = "std")]
136 {
137 GLOBAL_TIME_CONTEXT.get().copied()
138 }
139
140 #[cfg(all(not(feature = "std"), target_has_atomic = "8"))]
141 {
142 GLOBAL_TIME_CONTEXT.get()
143 }
144
145 #[cfg(all(not(feature = "std"), not(target_has_atomic = "8")))]
146 {
147 unsafe { core::ptr::read(core::ptr::addr_of!(GLOBAL_TIME_CONTEXT)) }
150 }
151}
152
153#[cfg(any(
154 not(feature = "std"),
155 all(feature = "std", target_family = "wasm", target_os = "unknown")
156))]
157pub(crate) fn panic_missing_system_time() -> ! {
158 panic!(
159 "no wall-clock time source is available; install one with universal_time::set_global_time_context()"
160 )
161}
162
163#[cfg(any(
164 not(feature = "std"),
165 all(feature = "std", target_family = "wasm", target_os = "unknown")
166))]
167pub(crate) fn panic_missing_instant() -> ! {
168 panic!(
169 "no monotonic clock is available; install one with universal_time::set_global_time_context()"
170 )
171}