oncelock/
sync.rs

1//! Implements the [`OnceLock`] and [`LazyLock`] types.
2
3use crate::polyfill::{FinishNonExhaustive, UntaggedOptionCell};
4use core::cell::UnsafeCell;
5use core::fmt::{Debug, Formatter};
6use core::mem::ManuallyDrop;
7use core::ops::Deref;
8
9#[cfg(sync_backend = "parking_lot")]
10mod parking_lot;
11#[cfg(sync_backend = "parking_lot")]
12use self::parking_lot as backend;
13#[cfg(sync_backend = "std")]
14mod std_sync;
15#[cfg(sync_backend = "std")]
16use self::std_sync as backend;
17#[cfg(sync_backend = "spin")]
18mod spin;
19#[cfg(sync_backend = "spin")]
20use self::spin as backend;
21
22/// If this is `true`, then [`OnceLock::get_or_init`] may be poisoned on panic.
23///
24/// This is necessary when using versions of `std` before 1.51 or versions of `spin` before v0.10,
25/// which don't support [`std::sync::Once::call_once_force`].
26pub const LEGACY_POISON_BEHAVIOR: bool = backend::LEGACY_POISON_BEHAVIOR;
27
28/// A thread-safe value which is initialized at most once.
29///
30/// Emulates the [`std::sync::OnceLock`] type on old versions of rust.
31///
32/// Reading a non-None value out of `OnceCell` establishes a happens-before relationship with a corresponding write,
33/// just like use of the [`std::sync::Once::call_once`] method.
34pub struct OnceLock<T> {
35    once: backend::Once,
36    value: UntaggedOptionCell<T>,
37}
38impl<T> Default for OnceLock<T> {
39    #[inline]
40    fn default() -> Self {
41        Self::new()
42    }
43}
44impl<T> OnceLock<T> {
45    /// Create a new `OnceLock` with no associated data.
46    #[inline]
47    #[rustversion::attr(since(1.32), const)] // const UnsafeCell::new
48    pub fn new() -> Self {
49        OnceLock {
50            once: backend::Once::new(),
51            value: UntaggedOptionCell::uninit(),
52        }
53    }
54
55    /// Initializes the contents of this cell to the specified value.
56    ///
57    /// May block if initialization is currently in progress.
58    ///
59    /// # Errors
60    /// Returns an error containing the passed value if the cell is already initialized,
61    /// or `Ok(())` if the value
62    #[inline]
63    pub fn set(&self, value: T) -> Result<(), T> {
64        let mut value = Some(value);
65        self.get_or_init(|| value.take().unwrap());
66        if let Some(failed) = value {
67            Err(failed)
68        } else {
69            Ok(())
70        }
71    }
72
73    /// Get the reference to the underlying value,
74    /// triggering undefined behavior if uninitialized.
75    ///
76    /// # Safety
77    /// Caller is responsible for ensuring the data is fully initialized,
78    /// and for avoiding data races with concurrent initialization.
79    ///
80    /// # Panics
81    /// In debug mode, this may panic if uninitialized.
82    #[inline]
83    pub unsafe fn get_unchecked(&self) -> &T {
84        debug_assert!(self.get().is_some(), "UB: access uninitialized data");
85        // SAFETY: Guaranteed by caller
86        unsafe { &*self.value.get_ptr_unchecked() }
87    }
88
89    /// Get the value associated with this lock,
90    /// or `None` if uninitialized.
91    #[inline]
92    pub fn get(&self) -> Option<&T> {
93        if self.once.is_completed() {
94            // SAFETY: Guaranteed to be initialized
95            Some(unsafe { self.get_unchecked() })
96        } else {
97            None
98        }
99    }
100
101    /// Get the contents of the cell if initialized,
102    /// otherwise using the specified callback to initialize the value.
103    ///
104    /// Even in the presence of multiple threads, only one initialization function is ever invoked.
105    ///
106    /// # Panics
107    /// If the [`LEGACY_POISON_BEHAVIOR`] flag is false, initialization will be retried if a thread panics.
108    /// If [`LEGACY_POISON_BEHAVIOR`] is true, this will be poisoned if an initialization function panics.
109    /// In that case, all feature calls will unconditionally panic (just like [`std::sync::Once::call_once`]).
110    #[inline]
111    pub fn get_or_init<F: FnOnce() -> T>(&self, f: F) -> &T {
112        self.get().unwrap_or_else(|| self.init_fallback(f))
113    }
114
115    #[cold]
116    fn init_fallback(&self, func: impl FnOnce() -> T) -> &T {
117        self.once.call_once_force(|| {
118            let value = func();
119            // SAFETY: Guaranteed to exclude all other threads,
120            // reads can only come after this `call_once` completes
121            unsafe {
122                self.value.write(value);
123            }
124        });
125        // SAFETY: Completion of call_once means we are initialized
126        unsafe { &*self.value.get_ptr_unchecked() }
127    }
128
129    /// Consumes the `OnceLock`, returning the wrapped value.
130    ///
131    /// Returns `None` if the cell was uninitialized.
132    #[inline]
133    pub fn into_inner(self) -> Option<T> {
134        let mut this = ManuallyDrop::new(self);
135        this.take()
136    }
137
138    /// Takes the value out of this `OnceLock`, moving it back to an uninitialized state.
139    ///
140    /// This is safe because it borrows the lock mutably.
141    #[inline]
142    pub fn take(&mut self) -> Option<T> {
143        if self.get().is_some() {
144            // A panic here would be UB
145            // Neither of these methods can possibly panic,
146            // but use a guard to make extra sure
147            let guard = AbortGuard;
148            struct AbortGuard;
149            impl Drop for AbortGuard {
150                fn drop(&mut self) {
151                    // double panic due to abort
152                    panic!("Panic here could cause invalid state to be seen");
153                }
154            }
155            // SAFETY: Value is guaranteed to be initialized because `get` returns Some
156            // Guard ensures that there is no panic before we reset our state
157            let value = unsafe { core::ptr::read(self.value.get_ptr_unchecked()) };
158            self.once = backend::Once::new();
159            core::mem::forget(guard);
160            Some(value)
161        } else {
162            None
163        }
164    }
165}
166impl<T> Drop for OnceLock<T> {
167    fn drop(&mut self) {
168        drop(self.take());
169    }
170}
171impl<T: Clone> Clone for OnceLock<T> {
172    fn clone(&self) -> Self {
173        match self.get() {
174            None => OnceLock::new(),
175            Some(inner) => OnceLock::from(inner.clone()),
176        }
177    }
178}
179// SAFETY: Valid since `T: Send`
180unsafe impl<T: Send> Send for OnceLock<T> {}
181// SAFETY: Valid since `T: Sync`, all mutation is otherwise synchronized
182unsafe impl<T: Sync> Sync for OnceLock<T> {}
183impl<T> From<T> for OnceLock<T> {
184    fn from(value: T) -> Self {
185        let res = OnceLock::new();
186        match res.set(value) {
187            Ok(()) => res,
188            Err(_) => {
189                // SAFETY: Not possible since we are the only user
190                unsafe { core::hint::unreachable_unchecked() }
191            }
192        }
193    }
194}
195
196enum LazyState<T, F> {
197    Initialized(T),
198    InProgress,
199    Uninitialized(F),
200}
201/// A lazily initialized value using a particular initialization function.
202///
203/// Unlike [`OnceLock`], this type is irrevocably poisoned if the initialization function panics.
204pub struct LazyLock<T, F = fn() -> T> {
205    once: backend::Once,
206    state: UnsafeCell<LazyState<T, F>>,
207}
208impl<T, F> LazyLock<T, F> {
209    /// Creates a new lazy value with the given initializing function.
210    ///
211    /// Logically, this should require [`F: FnOnce() -> T`](FnOnce),
212    /// but it cannot since that is not supported on the MSRV.
213    #[inline]
214    #[rustversion::attr(since(1.32), const)] // const UnsafeCell::new
215    pub fn new(func: F) -> Self {
216        LazyLock {
217            once: backend::Once::new(),
218            state: UnsafeCell::new(LazyState::Uninitialized(func)),
219        }
220    }
221
222    /// Get the value if the cell is initialized,
223    /// or `None` if not initialized yet.
224    #[inline]
225    pub fn get(this: &Self) -> Option<&T> {
226        if this.once.is_completed() {
227            // SAFETY: The `is_completed` method only returns true
228            // once initialization has finished.
229            Some(unsafe { Self::get_unchecked(this) })
230        } else {
231            None
232        }
233    }
234
235    #[inline]
236    unsafe fn get_unchecked(this: &Self) -> &T {
237        // SAFETY: Caller guarantees type is fully initialized,
238        // so not possible to observe these states
239        match unsafe { &*this.state.get() } {
240            LazyState::Uninitialized(_) | LazyState::InProgress => {
241                // SAFETY: Caller guarantees fully initialized, so not possible to encounter these
242                unsafe { core::hint::unreachable_unchecked() }
243            }
244            LazyState::Initialized(ref value) => value,
245        }
246    }
247}
248impl<T, F: FnOnce() -> T> LazyLock<T, F> {
249    /// Forces the evaluation of this lazy value and returns a reference to result.
250    /// This is equivalent to the Deref impl, but is explicit.
251    ///
252    /// This method will block the calling thread if another initialization routine is currently running.
253    ///
254    /// This function is implemented in terms of [`OnceLock::get_or_init`].
255    #[inline]
256    #[cfg_attr(has_track_caller, track_caller)]
257    pub fn force(this: &Self) -> &T {
258        Self::get(this).unwrap_or_else(|| Self::init_fallback(this))
259    }
260    #[cold]
261    #[cfg_attr(has_track_caller, track_caller)]
262    fn init_fallback(this: &Self) -> &T {
263        this.once.call_once(|| {
264            // SAFETY: Have exclusive access
265            // (recursive initialization will either deadlock or panic)
266            let this = unsafe { &mut *this.state.get() };
267            let func = match core::mem::replace(this, LazyState::InProgress) {
268                LazyState::Initialized(_) | LazyState::InProgress => unreachable!(),
269                LazyState::Uninitialized(func) => func,
270            };
271            *this = LazyState::Initialized(func());
272        });
273        // SAFETY: After call_once completes, we are fully initialized
274        unsafe { Self::get_unchecked(this) }
275    }
276}
277impl<T, F: FnOnce() -> T> Deref for LazyLock<T, F> {
278    type Target = T;
279
280    #[inline]
281    fn deref(&self) -> &Self::Target {
282        LazyLock::force(self)
283    }
284}
285
286impl<T: Debug, F> Debug for LazyLock<T, F> {
287    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
288        f.debug_struct("LazyLock")
289            .field("value", &Self::get(self))
290            .polyfill_finish_non_exhaustive()
291    }
292}