conquer_once/
cell.rs

1//! Generic definition and implementation of the [`OnceCell`] type.
2
3use core::{
4    cell::UnsafeCell,
5    fmt,
6    marker::PhantomData,
7    mem::{self, MaybeUninit},
8    ptr,
9    sync::atomic::Ordering,
10};
11
12use crate::{
13    state::{AtomicOnceState, BlockedState, OnceState, SwapState, TryBlockError},
14    POISON_PANIC_MSG,
15};
16
17/// An internal (sealed) trait specifying the unblocking mechanism of a cell
18/// locking strategy.
19pub trait Unblock {
20    /// Unblocks all waiting threads after setting the cell state to `READY`.
21    ///
22    /// # Safety
23    ///
24    /// Must only be called after swapping the cell with `READY` with the state
25    /// returned by the swap operation.
26    unsafe fn on_unblock(state: BlockedState);
27}
28
29/// An internal (sealed) trait specifying the blocking mechanism of a cell
30/// locking strategy.
31///
32/// # Safety
33///
34/// The implementation of `block` must adhere to its documentation or otherwise
35/// data-races could occur in other code.
36pub unsafe trait Block: Unblock {
37    /// Blocks the current thread until `state` is either `READY` or `POISONED`.
38    ///
39    /// # Panics
40    ///
41    /// Panics if the `state` becomes poisoned.
42    fn block(state: &AtomicOnceState);
43}
44
45/// An interior mutability cell type which allows synchronized one-time
46/// initialization and read-only access exclusively after initialization.
47///
48/// # Poisoning
49///
50/// A thread that panics in the course of executing its `init` function or
51/// closure **poisons** the cell.
52/// All subsequent accesses to a poisoned cell will propagate this and panic
53/// themselves.
54pub struct OnceCell<T, B> {
55    /// The current initialization status.
56    state: AtomicOnceState,
57    /// The internal and potentially uninitialized value.
58    inner: UnsafeCell<MaybeUninit<T>>,
59    /// A marker for the blocking strategy (i.e. OS-level block or spin-lock)
60    _marker: PhantomData<B>,
61}
62
63unsafe impl<T, B> Send for OnceCell<T, B> where T: Send {}
64unsafe impl<T, B> Sync for OnceCell<T, B> where T: Send + Sync {}
65
66impl<T, B> OnceCell<T, B> {
67    /// Creates a new uninitialized [`OnceCell`].
68    #[inline]
69    pub const fn uninit() -> Self {
70        Self {
71            state: AtomicOnceState::new(),
72            inner: UnsafeCell::new(MaybeUninit::uninit()),
73            _marker: PhantomData,
74        }
75    }
76
77    /// Creates a new [`OnceCell`] pre-initialized with `value`.
78    #[inline]
79    pub const fn new(value: T) -> Self {
80        Self {
81            state: AtomicOnceState::ready(),
82            inner: UnsafeCell::new(MaybeUninit::new(value)),
83            _marker: PhantomData,
84        }
85    }
86
87    /// Consumes `self` and returns a [`Some(T)`](Some) if the [`OnceCell`] has
88    /// previously been successfully initialized or [`None`] otherwise.
89    ///
90    /// # Panics
91    ///
92    /// This method panics if the [`OnceCell`] has been poisoned.
93    ///
94    /// # Examples
95    ///
96    /// ```
97    /// # use conquer_once::spin::OnceCell;
98    ///
99    /// let uninit: OnceCell<i32> = OnceCell::uninit();
100    /// assert!(uninit.into_inner().is_none());
101    ///
102    /// let once = OnceCell::uninit();
103    /// once.init_once(|| "initialized");
104    /// assert_eq!(once.into_inner(), Some("initialized"));
105    /// ```
106    #[inline]
107    pub fn into_inner(mut self) -> Option<T> {
108        // SAFETY: `take_inner` can not be called again after this, since we
109        // forget `self` right after calling it
110        let res = unsafe { self.take_inner(false) };
111        mem::forget(self);
112        res
113    }
114
115    /// Returns true if the [`OnceCell`] has been successfully initialized.
116    ///
117    /// This method will never panic or block.
118    #[inline]
119    pub fn is_initialized(&self) -> bool {
120        // (cell:1) this acquire load syncs-with the acq-rel swap (guard:2)
121        self.state.load(Ordering::Acquire) == Ok(OnceState::Ready)
122    }
123
124    /// Returns true if the [`OnceCell`] has been poisoned during
125    /// initialization.
126    ///
127    /// This method will never panic or block.
128    ///
129    /// Once this method has returned `true` all other means of accessing the
130    /// [`OnceCell`] except for further calls to
131    /// [`is_initialized`][OnceCell::is_initialized] or
132    /// [`is_poisoned`][OnceCell::is_poisoned] will panic.
133    #[inline]
134    pub fn is_poisoned(&self) -> bool {
135        self.state.load(Ordering::Relaxed).is_err()
136    }
137
138    /// Returns a reference to the [`OnceCell`]'s initialized inner state or
139    /// an [`Err`].
140    ///
141    /// This method will never blocks.
142    ///
143    /// When this function returns with an [`Ok`] result, it is guaranteed that
144    /// some initialization closure has run and completed.
145    /// It is also guaranteed that any memory writes performed by the executed
146    /// closure can be reliably observed by other threads at this point (there
147    /// is a happens-before relation between the closure and code executing
148    /// after the return).
149    ///
150    /// # Errors
151    ///
152    /// This method fails if the [`OnceCell`] is either not initialized
153    /// ([`Uninit`][TryGetError::Uninit]) or is currently being
154    /// initialized by some other thread
155    /// ([`WouldBlock`][TryGetError::WouldBlock]).
156    ///
157    /// # Panics
158    ///
159    /// This method panics if the [`OnceCell`] has been poisoned.
160    #[inline]
161    pub fn try_get(&self) -> Result<&T, TryGetError> {
162        // (cell:2) this acquire load syncs-with the acq-rel swap (guard:2)
163        match self.state.load(Ordering::Acquire).expect(POISON_PANIC_MSG) {
164            OnceState::Ready => Ok(unsafe { self.get_unchecked() }),
165            OnceState::Uninit => Err(TryGetError::Uninit),
166            OnceState::WouldBlock(_) => Err(TryGetError::WouldBlock),
167        }
168    }
169
170    /// Returns a reference to the inner value without checking whether the
171    /// [`OnceCell`] is actually initialized.
172    ///
173    /// # Safety
174    ///
175    /// The caller has to ensure that the cell has been successfully
176    /// initialized, otherwise uninitialized memory will be read.
177    ///
178    /// # Examples
179    ///
180    /// This is one safe way to use this method, although
181    /// [`try_get`][OnceCell::try_get] is the better alternative:
182    ///
183    /// ```
184    /// # #[cfg(feature = "std")]
185    /// use conquer_once::OnceCell;
186    /// # #[cfg(not(feature = "std"))]
187    /// # use conquer_once::spin::OnceCell;
188    ///
189    /// // let cell = ...
190    /// # let cell = OnceCell::uninit();
191    /// # cell.init_once(|| 0);
192    ///
193    /// let res = if cell.is_initialized() {
194    ///     Some(unsafe { cell.get_unchecked() })
195    /// } else {
196    ///     None
197    /// };
198    ///
199    /// # assert_eq!(res, Some(&0));
200    /// ```
201    #[inline]
202    pub unsafe fn get_unchecked(&self) -> &T {
203        let inner = &*self.inner.get();
204        &*inner.as_ptr()
205    }
206
207    /// Moves the inner cell value out of the [`OnceCell`] and returns it
208    /// wrapped in [`Some`], if it has been successfully initialized.
209    ///
210    /// # Safety
211    ///
212    /// If the cell is initialized, this must be called at most once.
213    ///
214    /// # Panics
215    ///
216    /// If `ignore_poisoning` is `false`, this method will panic if the
217    /// [`OnceCell`] has been poisoned, otherwise it will simply return
218    /// [`None`].
219    #[inline]
220    unsafe fn take_inner(&mut self, ignore_poisoning: bool) -> Option<T> {
221        #[allow(clippy::match_wild_err_arm)]
222        match self.state.load(Ordering::Relaxed) {
223            Err(_) if !ignore_poisoning => panic!("{}", POISON_PANIC_MSG),
224            // SAFETY: the mutable reference guarantees there can be no aliased
225            // reference and since the state is `Ready`
226            Ok(OnceState::Ready) =>
227            {
228                #[allow(unused_unsafe)]
229                Some(unsafe { ptr::read(self.get_unchecked()) })
230            }
231            _ => None,
232        }
233    }
234}
235
236impl<T, B: Unblock> OnceCell<T, B> {
237    /// Attempts to initialize the [`OnceCell`] with `func` if is is
238    /// uninitialized and returns [`Ok(())`](Ok) only if `func` is successfully
239    /// executed.
240    ///
241    /// This method will never block.
242    ///
243    /// When this function returns with an [`Ok`] or
244    /// [`Err(AlreadyInit)`][TryInitError::AlreadyInit] result, it is guaranteed
245    /// that *some* initialization closure has run and completed (it may not be
246    /// the closure specified).
247    /// It is also guaranteed that any memory writes performed by the executed
248    /// closure can be reliably observed by other threads at this point (there
249    /// is a *happens-before* relation between the closure and code executing
250    /// after the return).
251    ///
252    /// # Errors
253    ///
254    /// This method fails, if the initialization of [`OnceCell`] has already
255    /// been completed previously, in which case an
256    /// [`AlreadyInit`][TryInitError::AlreadyInit] error is returned.
257    /// If another thread is concurrently in the process of initializing it and
258    /// this thread would have to block, a
259    /// [`WouldBlock`][TryInitError::WouldBlock] error is returned.
260    ///
261    /// # Panics
262    ///
263    /// This method panics if the [`OnceCell`] has been poisoned.
264    ///
265    /// # Examples
266    ///
267    /// ```
268    /// # #[cfg(feature = "std")]
269    /// use conquer_once::{OnceCell, TryInitError};
270    /// # #[cfg(not(feature = "std"))]
271    /// # use conquer_once::{spin::OnceCell, TryInitError};
272    ///
273    /// let cell = OnceCell::uninit();
274    ///
275    /// // .. in thread 1
276    /// let res = cell.try_init_once(|| {
277    ///     1
278    /// });
279    /// assert!(res.is_ok());
280    ///
281    /// // .. in thread 2
282    /// let res = cell.try_init_once(|| {
283    ///     2
284    /// });
285    /// assert_eq!(res, Err(TryInitError::AlreadyInit));
286    ///
287    /// # assert_eq!(cell.get().copied(), Some(1));
288    /// ```
289    #[inline]
290    pub fn try_init_once(&self, func: impl FnOnce() -> T) -> Result<(), TryInitError> {
291        // (cell:3) this acq load syncs-with the acq-rel swap (guard:2)
292        match self.state.load(Ordering::Acquire).expect(POISON_PANIC_MSG) {
293            OnceState::Ready => Err(TryInitError::AlreadyInit),
294            OnceState::WouldBlock(_) => Err(TryInitError::WouldBlock),
295            OnceState::Uninit => {
296                let mut func = Some(func);
297                self.try_init_inner(&mut || func.take().unwrap()())?;
298                Ok(())
299            }
300        }
301    }
302
303    /// This method is annotated with `#[cold]` in order to keep it out of the
304    /// fast path.
305    #[inline(never)]
306    #[cold]
307    fn try_init_inner(&self, func: &mut dyn FnMut() -> T) -> Result<&T, TryBlockError> {
308        // sets the state to blocked (i.e. guarantees mutual exclusion) or
309        // returns with an error.
310        let guard = PanicGuard::<B>::try_block(&self.state)?;
311        // SAFETY: `try_block` ensures mutual exclusion, so no aliasing of the
312        // cell is possible and the raw pointer write is unproblematic (pointer
313        // is trivially aligned and valid)
314        unsafe {
315            let inner = &mut *self.inner.get();
316            inner.as_mut_ptr().write(func());
317        }
318        guard.disarm();
319
320        // SAFETY: the cell was just initialized so no check is required here
321        Ok(unsafe { self.get_unchecked() })
322    }
323
324    /// Returns a reference to the [`OnceCell`]'s initialized inner state or
325    /// otherwise attempts to initialize it with `func` and return the result.
326    ///
327    /// This method never blocks.
328    ///
329    /// When this function returns with an [`Ok`] result, it is guaranteed that
330    /// some initialization closure has run and completed (it may not be the
331    /// closure specified).
332    /// It is also guaranteed that any memory writes performed by the executed
333    /// closure can be reliably observed by other threads at this point (there
334    /// is a happens-before relation between the closure and code executing
335    /// after the return).
336    ///
337    /// # Errors
338    ///
339    /// This method only fails if the calling thread would have to block in case
340    /// another thread is concurrently initializing the [`OnceCell`].
341    ///
342    /// # Panics
343    ///
344    /// This method panics if the [`OnceCell`] has been poisoned.
345    #[inline]
346    pub fn try_get_or_init(&self, func: impl FnOnce() -> T) -> Result<&T, WouldBlockError> {
347        match self.try_get() {
348            Ok(res) => Ok(res),
349            Err(TryGetError::WouldBlock) => Err(WouldBlockError(())),
350            Err(TryGetError::Uninit) => {
351                let mut func = Some(func);
352                let res = self.try_init_inner(&mut || func.take().unwrap()())?;
353                Ok(res)
354            }
355        }
356    }
357}
358
359impl<T, B: Block> OnceCell<T, B> {
360    /// Returns a reference to the [`OnceCell`]'s initialized inner state or
361    /// [`None`].
362    ///
363    /// This method **blocks** if another thread has already begun initializing
364    /// the [`OnceCell`] concurrently.
365    /// See [`try_get`][OnceCell::try_get] for a non-blocking alternative.
366    ///
367    /// When this function returns with [`Some`], it is guaranteed that some
368    /// initialization closure has run and completed.
369    /// It is also guaranteed that any memory writes performed by the executed
370    /// closure can be reliably observed by other threads at this point (there
371    /// is a happens-before relation between the closure and code executing
372    /// after the return).
373    ///
374    /// # Panics
375    ///
376    /// This method panics if the [`OnceCell`] has been poisoned.
377    ///
378    /// # Examples
379    ///
380    /// ```
381    /// # #[cfg(feature = "std")]
382    /// use conquer_once::OnceCell;
383    /// # #[cfg(not(feature = "std"))]
384    /// # use conquer_once::spin::OnceCell;
385    ///
386    /// let cell = OnceCell::uninit();
387    /// assert_eq!(cell.get(), None);
388    /// cell.init_once(|| {
389    ///     1
390    /// });
391    /// assert_eq!(cell.get(), Some(&1));
392    /// ```
393    #[inline]
394    pub fn get(&self) -> Option<&T> {
395        match self.try_get() {
396            Ok(res) => Some(res),
397            Err(TryGetError::WouldBlock) => {
398                B::block(&self.state);
399                // SAFETY: `block` only returns when the state is set to
400                // `INITIALIZED` and acts as an acquire barrier (initialization
401                // happens-before block returns)
402                Some(unsafe { self.get_unchecked() })
403            }
404            Err(TryGetError::Uninit) => None,
405        }
406    }
407
408    /// Attempts to initialize the [`OnceCell`] with `func` if it is
409    /// uninitialized.
410    ///
411    /// This method **blocks** if another thread has already begun initializing
412    /// the [`OnceCell`] concurrently.
413    ///
414    /// If the initialization of the [`OnceCell`] has already been
415    /// completed previously, this method returns early with minimal
416    /// overhead.
417    ///
418    /// When this function returns, it is guaranteed that some initialization
419    /// closure has run and completed (it may not be the closure specified).
420    /// It is also guaranteed that any memory writes performed by the executed
421    /// closure can be reliably observed by other threads at this point (there
422    /// is a happens-before relation between the closure and code executing
423    /// after the return).
424    ///
425    /// # Panics
426    ///
427    /// This method panics if the [`OnceCell`] has been poisoned.
428    ///
429    /// # Examples
430    ///
431    /// ```
432    /// # #[cfg(feature = "std")]
433    /// use conquer_once::OnceCell;
434    /// # #[cfg(not(feature = "std"))]
435    /// # use conquer_once::spin::OnceCell;
436    ///
437    /// let cell = OnceCell::uninit();
438    /// cell.init_once(|| {
439    ///     // expensive calculation
440    ///     (0..1_000).map(|i| i * i).sum::<usize>()
441    /// });
442    ///
443    /// cell.init_once(|| {
444    ///     // any further or concurrent calls to `init_once` will do
445    ///     // nothing and return immediately with almost no overhead.
446    ///     # 0
447    /// });
448    ///
449    /// # let exp = (0..1_000).map(|i| i * i).sum::<usize>();
450    /// # assert_eq!(cell.get().copied(), Some(exp));
451    /// ```
452    #[inline]
453    pub fn init_once(&self, func: impl FnOnce() -> T) {
454        if let Err(TryInitError::WouldBlock) = self.try_init_once(func) {
455            // block the current thread if the cell is currently being
456            // initialized
457            B::block(&self.state);
458        }
459    }
460
461    /// Returns a reference to the [`OnceCell`]'s initialized inner state or
462    /// otherwise attempts to initialize it with `func` and return the result.
463    ///
464    /// This method **blocks** if another thread has already begun
465    /// initializing the [`OnceCell`] concurrently.
466    /// See [`try_get_or_init`][OnceCell::try_get_or_init] for a non-blocking
467    /// alternative.
468    ///
469    /// When this function returns, it is guaranteed that some initialization
470    /// closure has run and completed (it may not be the closure specified).
471    /// It is also guaranteed that any memory writes performed by the executed
472    /// closure can be reliably observed by other threads at this point (there
473    /// is a happens-before relation between the closure and code executing
474    /// after the return).
475    ///
476    /// # Panics
477    ///
478    /// This method panics if the [`OnceCell`] has been poisoned.
479    #[inline]
480    pub fn get_or_init(&self, func: impl FnOnce() -> T) -> &T {
481        match self.try_get_or_init(func) {
482            Ok(res) => res,
483            Err(_) => {
484                B::block(&self.state);
485                // SAFETY: `block` only returns when the state is set to
486                // `INITIALIZED` and acts as an acquire barrier (initialization
487                // happens-before block returns)
488                unsafe { self.get_unchecked() }
489            }
490        }
491    }
492}
493
494impl<T: fmt::Debug, B> fmt::Debug for OnceCell<T, B> {
495    #[inline]
496    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
497        f.debug_struct("OnceCell").field("inner", &self.try_get().ok()).finish()
498    }
499}
500
501impl<T, B> Drop for OnceCell<T, B> {
502    #[inline]
503    fn drop(&mut self) {
504        // drop must never panic, so poisoning is ignored
505        // SAFETY: take_inner cannot be called again after drop has been called
506        mem::drop(unsafe { self.take_inner(true) })
507    }
508}
509
510const UNINIT_MSG: &str = "the `OnceCell` is uninitialized";
511const ALREADY_INIT_MSG: &str = "the `OnceCell` has already been initialized";
512const WOULD_BLOCK_MSG: &str = "the `OnceCell` is currently being initialized";
513
514/// Possible error variants of non-blocking initialization calls.
515#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
516pub enum TryInitError {
517    /// The [`OnceCell`] is already initialized and the initialization procedure
518    /// was not called.
519    AlreadyInit,
520    /// The [`OnceCell`] is currently being initialized by another thread and
521    /// the current thread would have to block.
522    WouldBlock,
523}
524
525impl fmt::Display for TryInitError {
526    #[inline]
527    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
528        match self {
529            TryInitError::AlreadyInit => write!(f, "{}", ALREADY_INIT_MSG),
530            TryInitError::WouldBlock => write!(f, "{}", WOULD_BLOCK_MSG),
531        }
532    }
533}
534
535impl From<TryBlockError> for TryInitError {
536    #[inline]
537    fn from(err: TryBlockError) -> Self {
538        match err {
539            TryBlockError::AlreadyInit => TryInitError::AlreadyInit,
540            TryBlockError::WouldBlock(_) => TryInitError::WouldBlock,
541        }
542    }
543}
544
545#[cfg(feature = "std")]
546impl std::error::Error for TryInitError {}
547
548/// Possible error variants of non-blocking fallible get calls.
549#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
550pub enum TryGetError {
551    /// The [`OnceCell`] is currently not initialized.
552    Uninit,
553    /// The [`OnceCell`] is currently being initialized by another thread and
554    /// the current thread would have to block.
555    WouldBlock,
556}
557
558impl fmt::Display for TryGetError {
559    #[inline]
560    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
561        match self {
562            TryGetError::Uninit => write!(f, "{}", UNINIT_MSG),
563            TryGetError::WouldBlock => write!(f, "{}", WOULD_BLOCK_MSG),
564        }
565    }
566}
567
568#[cfg(feature = "std")]
569impl std::error::Error for TryGetError {}
570
571/// An error indicating that a [`OnceCell`] would have to block.
572#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
573pub struct WouldBlockError(());
574
575impl fmt::Display for WouldBlockError {
576    #[inline]
577    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
578        write!(f, "{}", WOULD_BLOCK_MSG)
579    }
580}
581
582impl From<TryBlockError> for WouldBlockError {
583    #[inline]
584    fn from(err: TryBlockError) -> Self {
585        match err {
586            TryBlockError::AlreadyInit => unreachable!(),
587            TryBlockError::WouldBlock(_) => Self(()),
588        }
589    }
590}
591
592#[cfg(feature = "std")]
593impl std::error::Error for WouldBlockError {}
594
595/// A guard for catching panics during the execution of the initialization
596/// closure.
597#[derive(Debug)]
598struct PanicGuard<'a, B: Unblock> {
599    /// The state of the associated [`OnceCell`].
600    state: &'a AtomicOnceState,
601    /// Flag for indicating if a panic has occurred during the caller supplied
602    /// arbitrary closure.
603    poison: bool,
604    /// A marker for the [`OnceCell`]'s blocking strategy.
605    _marker: PhantomData<B>,
606}
607
608impl<'a, B: Unblock> PanicGuard<'a, B> {
609    /// Attempts to block the [`OnceCell`] and return a guard on success.
610    #[inline]
611    fn try_block(state: &'a AtomicOnceState) -> Result<Self, TryBlockError> {
612        // (guard:1) this acquire CAS syncs-with the acq-rel swap (guard:2) and the acq-rel CAS
613        // (wait:2)
614        state.try_block(Ordering::Acquire)?;
615        Ok(Self { state, poison: true, _marker: PhantomData })
616    }
617
618    /// Consumes the guard and assures that no panic has occurred.
619    #[inline]
620    fn disarm(mut self) {
621        self.poison = false;
622        mem::drop(self);
623    }
624}
625
626impl<B: Unblock> Drop for PanicGuard<'_, B> {
627    #[inline]
628    fn drop(&mut self) {
629        let swap = if self.poison { SwapState::Poisoned } else { SwapState::Ready };
630        unsafe {
631            // (guard:2) this acq-rel swap syncs-with the acq-rel CAS (wait:2)
632            // and the acquire loads (cell:1), (cell:2), (wait:1) and the
633            // acquire CAS (guard:1)
634            let prev = self.state.unblock(swap, Ordering::AcqRel);
635            B::on_unblock(prev);
636        }
637    }
638}