1# ![doc = include_str!("../README.md")]
24#![cfg_attr(feature = "no_std", no_std)]
25#[cfg(all(not(feature = "no_std"), test))]
26mod tests;
27
28#[cfg(feature = "alloc")]
29extern crate alloc;
30
31use ::core::{
32 cell::UnsafeCell,
33 error::Error,
34 fmt::Display,
35 ops::Deref,
36 sync::atomic::{AtomicUsize, Ordering},
37};
38#[cfg(feature = "alloc")]
39use alloc::boxed::Box;
40use core::fmt::Debug;
41
42#[derive(Debug)]
43pub enum OnceInitError {
46 DataUninitialized,
48 DataInitialized,
50}
51
52impl Display for OnceInitError {
53 #[inline]
54 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
55 match self {
56 OnceInitError::DataUninitialized => f.write_str("data is uninitialized."),
57 OnceInitError::DataInitialized => f.write_str("data has already been initialized."),
58 }
59 }
60}
61impl Error for OnceInitError {}
62#[derive(Debug)]
63#[repr(usize)]
64pub enum OnceInitState {
67 UNINITIALIZED = 0,
69 INITIALIZED = 2,
71}
72
73const UNINITIALIZED: usize = 0;
74const INITIALIZING: usize = 1;
75const INITIALIZED: usize = 2;
76
77pub struct OnceInit<T: ?Sized + 'static>
84where
85 &'static T: Sized,
86{
87 state: AtomicUsize,
88 data: UnsafeCell<Option<&'static T>>,
89}
90
91impl<T: ?Sized> OnceInit<T> {
92 #[inline]
94 pub const fn uninit() -> Self {
95 Self {
96 state: AtomicUsize::new(UNINITIALIZED),
97 data: UnsafeCell::new(None),
98 }
99 }
100 #[inline]
102 pub const fn new(data: &'static T) -> Self
103 where
104 &'static T: Sized,
105 Self: Sized,
106 {
107 Self {
108 state: AtomicUsize::new(INITIALIZED),
109 data: UnsafeCell::new(Some(data)),
110 }
111 }
112 #[inline]
116 pub fn get(&self) -> Result<&'static T, OnceInitError> {
117 match self.state.load(Ordering::Acquire) {
118 INITIALIZED => Ok(unsafe { (*self.data.get()).unwrap_unchecked() }),
119 INITIALIZING => {
120 while self.state.load(Ordering::SeqCst) == INITIALIZING {
121 core::hint::spin_loop()
122 }
123 Ok(unsafe { (*self.data.get()).unwrap_unchecked() })
124 }
125 _ => Err(OnceInitError::DataUninitialized),
126 }
127 }
128 #[inline]
132 pub fn get_or_default(&self) -> &'static T
133 where
134 T: StaticDefault,
135 {
136 self.get().unwrap_or_else(|_| T::static_default())
137 }
138 #[inline]
148 pub unsafe fn get_unchecked(&self) -> &'static T {
149 unsafe { (*self.data.get()).unwrap_unchecked() }
150 }
151 pub fn state(&self) -> OnceInitState {
153 match self.state.load(Ordering::Acquire) {
154 UNINITIALIZED => OnceInitState::UNINITIALIZED,
155 INITIALIZING => {
156 while self.state.load(Ordering::SeqCst) == INITIALIZING {
157 core::hint::spin_loop()
158 }
159 OnceInitState::UNINITIALIZED
160 }
161 INITIALIZED => OnceInitState::INITIALIZED,
162 _ => unreachable!(),
163 }
164 }
165 fn init_internal<F>(&self, make_data: F) -> Result<(), OnceInitError>
166 where
167 F: FnOnce() -> &'static T,
168 {
169 let old_state = match self.state.compare_exchange(
170 UNINITIALIZED,
171 INITIALIZING,
172 Ordering::SeqCst,
173 Ordering::SeqCst,
174 ) {
175 Ok(s) | Err(s) => s,
176 };
177 match old_state {
178 INITIALIZING => {
179 while self.state.load(Ordering::SeqCst) == INITIALIZING {
180 core::hint::spin_loop()
181 }
182 Err(OnceInitError::DataInitialized)
183 }
184 INITIALIZED => Err(OnceInitError::DataInitialized),
185 _ => {
186 unsafe { *self.data.get() = Some(make_data()) }
187 self.state.store(INITIALIZED, Ordering::SeqCst);
188 Ok(())
189 }
190 }
191 }
192 #[inline]
196 pub fn init(&self, data: &'static T) -> Result<(), OnceInitError> {
197 self.init_internal(|| data)
198 }
199 #[inline]
201 #[cfg(any(feature = "alloc", not(feature = "no_std")))]
202 pub fn init_boxed(&self, data: Box<T>) -> Result<(), OnceInitError> {
203 self.init_internal(|| Box::leak(data))
204 }
205}
206unsafe impl<T> Sync for OnceInit<T> where T: ?Sized + Sync {}
207impl<T> Default for OnceInit<T>
208where
209 T: ?Sized + StaticDefault,
210 Self: Sized,
211{
212 #[inline]
213 fn default() -> Self {
214 Self::new(T::static_default())
215 }
216}
217impl<T: ?Sized + Debug> Debug for OnceInit<T> {
218 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
219 let mut d = f.debug_tuple("OnceInit");
220 match self.get().ok() {
221 Some(data) => d.field(&data),
222 None => d.field(&format_args!("<uninit>")),
223 };
224 d.finish()
225 }
226}
227
228pub unsafe trait StaticDefault {
243 fn static_default() -> &'static Self;
245}
246impl<T: ?Sized + StaticDefault> Deref for OnceInit<T> {
247 type Target = T;
248
249 #[inline]
250 fn deref(&self) -> &'static Self::Target {
251 self.get_or_default()
252 }
253}
254pub trait UninitGlobalHolder<T: ?Sized> {
256 fn init(&self, data: &'static T) -> Result<(), OnceInitError>;
258 #[cfg(any(feature = "alloc", not(feature = "no_std")))]
260 fn init_boxed(&self, data: Box<T>) -> Result<(), OnceInitError>;
261}
262impl<T: ?Sized> UninitGlobalHolder<T> for OnceInit<T> {
263 #[inline]
267 fn init(&self, data: &'static T) -> Result<(), OnceInitError> {
268 OnceInit::init(self, data)
269 }
270 #[inline]
272 fn init_boxed(&self, data: Box<T>) -> Result<(), OnceInitError> {
273 OnceInit::init_boxed(self, data)
274 }
275}
276pub trait UninitGlobal<T: ?Sized, M> {
280 fn holder() -> &'static M;
281 #[inline]
282 fn init(data: &'static T) -> Result<(), OnceInitError>
283 where
284 M: UninitGlobalHolder<T> + 'static,
285 {
286 Self::holder().init(data)
287 }
288 #[inline]
289 #[cfg(any(feature = "alloc", not(feature = "no_std")))]
290 fn init_boxed(data: Box<T>) -> Result<(), OnceInitError>
291 where
292 M: UninitGlobalHolder<T> + 'static,
293 {
294 Self::holder().init_boxed(data)
295 }
296}