crossbeam_utils/sync/
once_lock.rs

1// Based on unstable std::sync::OnceLock.
2//
3// Source: https://github.com/rust-lang/rust/blob/8e9c93df464b7ada3fc7a1c8ccddd9dcb24ee0a0/library/std/src/sync/once_lock.rs
4
5use core::cell::UnsafeCell;
6use core::mem::MaybeUninit;
7use core::sync::atomic::{AtomicBool, Ordering};
8use std::sync::Once;
9
10pub(crate) struct OnceLock<T> {
11    once: Once,
12    // Once::is_completed requires Rust 1.43, so use this to track of whether they have been initialized.
13    is_initialized: AtomicBool,
14    value: UnsafeCell<MaybeUninit<T>>,
15    // Unlike std::sync::OnceLock, we don't need PhantomData here because
16    // we don't use #[may_dangle].
17}
18
19unsafe impl<T: Sync + Send> Sync for OnceLock<T> {}
20unsafe impl<T: Send> Send for OnceLock<T> {}
21
22impl<T> OnceLock<T> {
23    /// Creates a new empty cell.
24    #[must_use]
25    pub(crate) const fn new() -> Self {
26        Self {
27            once: Once::new(),
28            is_initialized: AtomicBool::new(false),
29            value: UnsafeCell::new(MaybeUninit::uninit()),
30        }
31    }
32
33    /// Gets the contents of the cell, initializing it with `f` if the cell
34    /// was empty.
35    ///
36    /// Many threads may call `get_or_init` concurrently with different
37    /// initializing functions, but it is guaranteed that only one function
38    /// will be executed.
39    ///
40    /// # Panics
41    ///
42    /// If `f` panics, the panic is propagated to the caller, and the cell
43    /// remains uninitialized.
44    ///
45    /// It is an error to reentrantly initialize the cell from `f`. The
46    /// exact outcome is unspecified. Current implementation deadlocks, but
47    /// this may be changed to a panic in the future.
48    pub(crate) fn get_or_init<F>(&self, f: F) -> &T
49    where
50        F: FnOnce() -> T,
51    {
52        // Fast path check
53        if self.is_initialized() {
54            // SAFETY: The inner value has been initialized
55            return unsafe { self.get_unchecked() };
56        }
57        self.initialize(f);
58
59        debug_assert!(self.is_initialized());
60
61        // SAFETY: The inner value has been initialized
62        unsafe { self.get_unchecked() }
63    }
64
65    #[inline]
66    fn is_initialized(&self) -> bool {
67        self.is_initialized.load(Ordering::Acquire)
68    }
69
70    #[cold]
71    fn initialize<F>(&self, f: F)
72    where
73        F: FnOnce() -> T,
74    {
75        let slot = self.value.get().cast::<T>();
76        let is_initialized = &self.is_initialized;
77
78        self.once.call_once(|| {
79            let value = f();
80            unsafe {
81                slot.write(value);
82            }
83            is_initialized.store(true, Ordering::Release);
84        });
85    }
86
87    /// # Safety
88    ///
89    /// The value must be initialized
90    unsafe fn get_unchecked(&self) -> &T {
91        debug_assert!(self.is_initialized());
92        &*self.value.get().cast::<T>()
93    }
94}
95
96impl<T> Drop for OnceLock<T> {
97    fn drop(&mut self) {
98        if self.is_initialized() {
99            // SAFETY: The inner value has been initialized
100            unsafe { self.value.get().cast::<T>().drop_in_place() };
101        }
102    }
103}