lazyinit/
lib.rs

1#![cfg_attr(not(test), no_std)]
2#![doc = include_str!("../README.md")]
3
4use core::cell::UnsafeCell;
5use core::fmt;
6use core::hint::spin_loop;
7use core::mem::MaybeUninit;
8use core::ops::{Deref, DerefMut};
9use core::sync::atomic::{AtomicU8, Ordering};
10
11/// Not initialized yet.
12const UNINIT: u8 = 0;
13/// Initialization in progress.
14const INITIALIZING: u8 = 1;
15/// Successfully initialized.
16const INITED: u8 = 2;
17
18/// A wrapper of a lazy initialized value.
19///
20/// It implements [`Deref`] and [`DerefMut`]. The caller must use the dereference
21/// operation after initialization, otherwise it will panic.
22pub struct LazyInit<T> {
23    inited: AtomicU8,
24    data: UnsafeCell<MaybeUninit<T>>,
25}
26
27unsafe impl<T: Send + Sync> Sync for LazyInit<T> {}
28unsafe impl<T: Send> Send for LazyInit<T> {}
29
30impl<T> LazyInit<T> {
31    /// Creates a new uninitialized value.
32    pub const fn new() -> Self {
33        Self {
34            inited: AtomicU8::new(UNINIT),
35            data: UnsafeCell::new(MaybeUninit::uninit()),
36        }
37    }
38
39    /// Initializes the value once and only once.
40    ///
41    /// # Panics
42    ///
43    /// Panics if the value is already initialized.
44    pub fn init_once(&self, data: T) -> &T {
45        self.call_once(|| data).expect("Already initialized")
46    }
47
48    /// Performs an initialization routine once and only once.
49    ///
50    /// If the value is already initialized, the function will not be called
51    /// and a [`None`] will be returned.
52    pub fn call_once<F>(&self, f: F) -> Option<&T>
53    where
54        F: FnOnce() -> T,
55    {
56        // Fast path check
57        if self.is_inited() {
58            return None;
59        }
60        loop {
61            match self.inited.compare_exchange_weak(
62                UNINIT,
63                INITIALIZING,
64                Ordering::Acquire,
65                Ordering::Relaxed,
66            ) {
67                Ok(_) => {
68                    let value = f();
69                    unsafe { (*self.data.get()).as_mut_ptr().write(value) };
70                    self.inited.store(INITED, Ordering::Release);
71                    return Some(unsafe { self.force_get() });
72                }
73                Err(INITIALIZING) => {
74                    while self.inited.load(Ordering::Acquire) == INITIALIZING {
75                        spin_loop();
76                    }
77                    return None;
78                }
79                Err(INITED) => {
80                    return None;
81                }
82                Err(UNINIT) => {
83                    continue;
84                }
85                _ => unreachable!(),
86            }
87        }
88    }
89
90    /// Checks whether the value is initialized.
91    #[inline]
92    pub fn is_inited(&self) -> bool {
93        self.inited.load(Ordering::Acquire) == INITED
94    }
95
96    /// Gets a reference to the value.
97    ///
98    /// Returns [`None`] if the value is not initialized.
99    pub fn get(&self) -> Option<&T> {
100        if self.is_inited() {
101            Some(unsafe { self.force_get() })
102        } else {
103            None
104        }
105    }
106
107    /// Gets a mutable reference to the value.
108    ///
109    /// Returns [`None`] if the value is not initialized.
110    pub fn get_mut(&mut self) -> Option<&mut T> {
111        if self.is_inited() {
112            Some(unsafe { self.force_get_mut() })
113        } else {
114            None
115        }
116    }
117
118    /// Gets the reference to the value without checking if it is initialized.
119    ///
120    /// # Safety
121    ///
122    /// Must be called after initialization.
123    #[inline]
124    pub unsafe fn get_unchecked(&self) -> &T {
125        debug_assert!(self.is_inited());
126        self.force_get()
127    }
128
129    /// Get a mutable reference to the value without checking if it is initialized.
130    ///
131    /// # Safety
132    ///
133    /// Must be called after initialization.
134    #[inline]
135    pub unsafe fn get_mut_unchecked(&mut self) -> &mut T {
136        debug_assert!(self.is_inited());
137        self.force_get_mut()
138    }
139
140    #[inline]
141    unsafe fn force_get(&self) -> &T {
142        (*self.data.get()).assume_init_ref()
143    }
144
145    #[inline]
146    unsafe fn force_get_mut(&mut self) -> &mut T {
147        (*self.data.get()).assume_init_mut()
148    }
149
150    fn panic_message(&self) -> ! {
151        panic!(
152            "Use uninitialized value: {:?}",
153            core::any::type_name::<Self>()
154        )
155    }
156}
157
158impl<T: fmt::Debug> fmt::Debug for LazyInit<T> {
159    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
160        match self.get() {
161            Some(s) => write!(f, "LazyInit {{ data: ")
162                .and_then(|()| s.fmt(f))
163                .and_then(|()| write!(f, "}}")),
164            None => write!(f, "LazyInit {{ <uninitialized> }}"),
165        }
166    }
167}
168
169impl<T> Default for LazyInit<T> {
170    fn default() -> Self {
171        Self::new()
172    }
173}
174
175impl<T> Deref for LazyInit<T> {
176    type Target = T;
177    #[inline]
178    fn deref(&self) -> &T {
179        if self.is_inited() {
180            unsafe { self.force_get() }
181        } else {
182            self.panic_message()
183        }
184    }
185}
186
187impl<T> DerefMut for LazyInit<T> {
188    #[inline]
189    fn deref_mut(&mut self) -> &mut T {
190        if self.is_inited() {
191            unsafe { self.force_get_mut() }
192        } else {
193            self.panic_message()
194        }
195    }
196}
197
198impl<T> Drop for LazyInit<T> {
199    fn drop(&mut self) {
200        if self.is_inited() {
201            unsafe { core::ptr::drop_in_place((*self.data.get()).as_mut_ptr()) };
202        }
203    }
204}
205
206#[cfg(test)]
207mod tests {
208    use super::*;
209    use std::thread;
210    use std::time::Duration;
211
212    #[test]
213    fn lazyinit_basic() {
214        static VALUE: LazyInit<u32> = LazyInit::new();
215        assert!(!VALUE.is_inited());
216        assert_eq!(VALUE.get(), None);
217
218        VALUE.init_once(233);
219        assert!(VALUE.is_inited());
220        assert_eq!(*VALUE, 233);
221        assert_eq!(VALUE.get(), Some(&233));
222    }
223
224    #[test]
225    #[should_panic]
226    fn panic_on_deref_before_init() {
227        static VALUE: LazyInit<u32> = LazyInit::new();
228        let _ = *VALUE;
229    }
230
231    #[test]
232    #[should_panic]
233    fn panic_on_double_init() {
234        static VALUE: LazyInit<u32> = LazyInit::new();
235        VALUE.init_once(1);
236        VALUE.init_once(2);
237    }
238
239    #[test]
240    fn lazyinit_concurrent() {
241        const N: usize = 16;
242        static VALUE: LazyInit<usize> = LazyInit::new();
243
244        let threads: Vec<_> = (0..N)
245            .map(|i| {
246                thread::spawn(move || {
247                    thread::sleep(Duration::from_millis(10));
248                    VALUE.call_once(|| i)
249                })
250            })
251            .collect();
252
253        let mut ok = 0;
254        for (i, thread) in threads.into_iter().enumerate() {
255            if thread.join().unwrap().is_some() {
256                ok += 1;
257                assert_eq!(*VALUE, i);
258            }
259        }
260        assert_eq!(ok, 1);
261    }
262    #[test]
263    fn lazyinit_get_unchecked() {
264        static VALUE: LazyInit<u32> = LazyInit::new();
265        VALUE.init_once(123);
266        let v = unsafe { VALUE.get_unchecked() };
267        assert_eq!(*v, 123);
268    }
269
270    #[test]
271    fn lazyinit_get_mut_unchecked() {
272        static mut VALUE: LazyInit<u32> = LazyInit::new();
273        unsafe {
274            VALUE.init_once(123);
275        }
276        let v = unsafe { VALUE.get_mut_unchecked() };
277        *v += 3;
278        assert_eq!(*v, 126);
279    }
280}