Skip to main content

dtact_util/sync/
once_cell.rs

1//! Async once-initialized cell.
2
3use super::wait_queue::WaitQueue;
4use std::cell::UnsafeCell;
5use std::future::Future;
6use std::sync::atomic::{AtomicU8, Ordering};
7use std::task::{Context, Poll};
8
9const UNINIT: u8 = 0;
10const INITIALIZING: u8 = 1;
11const INIT: u8 = 2;
12
13/// A cell that's initialized at most once, asynchronously.
14///
15/// Concurrent callers of [`OnceCell::get_or_init`] that race to be first
16/// all wait for the same single initialization to complete rather than
17/// each running their own initializer.
18#[repr(align(64))]
19pub struct OnceCell<T> {
20    state: AtomicU8,
21    data: UnsafeCell<Option<T>>,
22    wait: WaitQueue,
23}
24
25// SAFETY: `state`'s CAS transitions are the sole gate on `data` access —
26// exactly one caller ever holds `INITIALIZING` and writes `data`, every
27// other reader only reads `data` after observing `INIT`.
28unsafe impl<T: Send> Send for OnceCell<T> {}
29unsafe impl<T: Send + Sync> Sync for OnceCell<T> {}
30
31impl<T> Default for OnceCell<T> {
32    fn default() -> Self {
33        Self::new()
34    }
35}
36
37impl<T> OnceCell<T> {
38    /// Create an empty, uninitialized cell.
39    #[must_use]
40    pub const fn new() -> Self {
41        Self {
42            state: AtomicU8::new(UNINIT),
43            data: UnsafeCell::new(None),
44            wait: WaitQueue::new(),
45        }
46    }
47
48    /// Create a cell already initialized with `value`.
49    #[must_use]
50    pub const fn new_with(value: T) -> Self {
51        Self {
52            state: AtomicU8::new(INIT),
53            data: UnsafeCell::new(Some(value)),
54            wait: WaitQueue::new(),
55        }
56    }
57
58    /// The cell's value, if it's already been initialized.
59    #[must_use]
60    #[inline(always)]
61    pub fn get(&self) -> Option<&T> {
62        (self.state.load(Ordering::Acquire) == INIT)
63            // SAFETY: `INIT` observed under Acquire pairs with the
64            // Release store in `get_or_init`/`set` that wrote `data`.
65            .then(|| unsafe { (*self.data.get()).as_ref() })
66            .flatten()
67    }
68
69    /// Set the cell's value if it isn't already initialized.
70    ///
71    /// # Errors
72    /// Returns `value` back if the cell was already initialized (or is
73    /// concurrently being initialized by [`Self::get_or_init`]).
74    #[inline(always)]
75    pub fn set(&self, value: T) -> Result<(), T> {
76        if self
77            .state
78            .compare_exchange(UNINIT, INITIALIZING, Ordering::AcqRel, Ordering::Acquire)
79            .is_err()
80        {
81            return Err(value);
82        }
83        // SAFETY: we hold the sole `INITIALIZING` token for this cell.
84        unsafe {
85            *self.data.get() = Some(value);
86        }
87        self.state.store(INIT, Ordering::Release);
88        self.wait.wake_all();
89        Ok(())
90    }
91
92    /// Return the cell's value, initializing it with (the result of
93    /// awaiting) `init` first if necessary. Concurrent callers racing to
94    /// initialize the same cell all observe the *same* initialization —
95    /// only the winner's `init` future actually runs.
96    pub async fn get_or_init<F, Fut>(&self, init: F) -> &T
97    where
98        F: FnOnce() -> Fut,
99        Fut: Future<Output = T>,
100        T: Send + Sync,
101    {
102        loop {
103            if let Some(v) = self.get() {
104                return v;
105            }
106            if self
107                .state
108                .compare_exchange(UNINIT, INITIALIZING, Ordering::AcqRel, Ordering::Acquire)
109                .is_ok()
110            {
111                let value = init().await;
112                // SAFETY: we hold the sole `INITIALIZING` token, so
113                // writing `data` then publishing `INIT` (and reading the
114                // just-written reference back out) is exclusive — no
115                // `.expect()`/panic path needed the way going back
116                // through `self.get()` would require.
117                let value_ref = unsafe {
118                    let slot = &mut *self.data.get();
119                    *slot = Some(value);
120                    slot.as_ref().unwrap_unchecked()
121                };
122                self.state.store(INIT, Ordering::Release);
123                self.wait.wake_all();
124                return value_ref;
125            }
126            // Someone else is initializing (or just finished, in which
127            // case `self.get()` at the top of the next iteration returns
128            // immediately) — wait for them.
129            std::future::poll_fn(|cx| self.poll_wait_for_init(cx)).await;
130        }
131    }
132
133    #[inline]
134    fn poll_wait_for_init(&self, cx: &Context<'_>) -> Poll<()> {
135        if self.state.load(Ordering::Acquire) == INIT {
136            return Poll::Ready(());
137        }
138        let token = self.wait.register(cx.waker());
139        if self.state.load(Ordering::Acquire) == INIT {
140            self.wait.cancel(token);
141            return Poll::Ready(());
142        }
143        Poll::Pending
144    }
145
146    /// Consume the cell, returning its value if initialized.
147    #[inline(always)]
148    pub fn into_inner(self) -> Option<T> {
149        self.data.into_inner()
150    }
151}