xref: /third_party/rust/crates/once_cell/src/race.rs (revision ef40d7f6)
1//! Thread-safe, non-blocking, "first one wins" flavor of `OnceCell`.
2//!
3//! If two threads race to initialize a type from the `race` module, they
4//! don't block, execute initialization function together, but only one of
5//! them stores the result.
6//!
7//! This module does not require `std` feature.
8//!
9//! # Atomic orderings
10//!
11//! All types in this module use `Acquire` and `Release`
12//! [atomic orderings](Ordering) for all their operations. While this is not
13//! strictly necessary for types other than `OnceBox`, it is useful for users as
14//! it allows them to be certain that after `get` or `get_or_init` returns on
15//! one thread, any side-effects caused by the setter thread prior to them
16//! calling `set` or `get_or_init` will be made visible to that thread; without
17//! it, it's possible for it to appear as if they haven't happened yet from the
18//! getter thread's perspective. This is an acceptable tradeoff to make since
19//! `Acquire` and `Release` have very little performance overhead on most
20//! architectures versus `Relaxed`.
21
22#[cfg(feature = "critical-section")]
23use atomic_polyfill as atomic;
24#[cfg(not(feature = "critical-section"))]
25use core::sync::atomic;
26
27use atomic::{AtomicUsize, Ordering};
28use core::cell::UnsafeCell;
29use core::marker::PhantomData;
30use core::num::NonZeroUsize;
31
32/// A thread-safe cell which can be written to only once.
33#[derive(Default, Debug)]
34pub struct OnceNonZeroUsize {
35    inner: AtomicUsize,
36}
37
38impl OnceNonZeroUsize {
39    /// Creates a new empty cell.
40    #[inline]
41    pub const fn new() -> OnceNonZeroUsize {
42        OnceNonZeroUsize { inner: AtomicUsize::new(0) }
43    }
44
45    /// Gets the underlying value.
46    #[inline]
47    pub fn get(&self) -> Option<NonZeroUsize> {
48        let val = self.inner.load(Ordering::Acquire);
49        NonZeroUsize::new(val)
50    }
51
52    /// Sets the contents of this cell to `value`.
53    ///
54    /// Returns `Ok(())` if the cell was empty and `Err(())` if it was
55    /// full.
56    #[inline]
57    pub fn set(&self, value: NonZeroUsize) -> Result<(), ()> {
58        let exchange =
59            self.inner.compare_exchange(0, value.get(), Ordering::AcqRel, Ordering::Acquire);
60        match exchange {
61            Ok(_) => Ok(()),
62            Err(_) => Err(()),
63        }
64    }
65
66    /// Gets the contents of the cell, initializing it with `f` if the cell was
67    /// empty.
68    ///
69    /// If several threads concurrently run `get_or_init`, more than one `f` can
70    /// be called. However, all threads will return the same value, produced by
71    /// some `f`.
72    pub fn get_or_init<F>(&self, f: F) -> NonZeroUsize
73    where
74        F: FnOnce() -> NonZeroUsize,
75    {
76        enum Void {}
77        match self.get_or_try_init(|| Ok::<NonZeroUsize, Void>(f())) {
78            Ok(val) => val,
79            Err(void) => match void {},
80        }
81    }
82
83    /// Gets the contents of the cell, initializing it with `f` if
84    /// the cell was empty. If the cell was empty and `f` failed, an
85    /// error is returned.
86    ///
87    /// If several threads concurrently run `get_or_init`, more than one `f` can
88    /// be called. However, all threads will return the same value, produced by
89    /// some `f`.
90    pub fn get_or_try_init<F, E>(&self, f: F) -> Result<NonZeroUsize, E>
91    where
92        F: FnOnce() -> Result<NonZeroUsize, E>,
93    {
94        let val = self.inner.load(Ordering::Acquire);
95        let res = match NonZeroUsize::new(val) {
96            Some(it) => it,
97            None => {
98                let mut val = f()?.get();
99                let exchange =
100                    self.inner.compare_exchange(0, val, Ordering::AcqRel, Ordering::Acquire);
101                if let Err(old) = exchange {
102                    val = old;
103                }
104                unsafe { NonZeroUsize::new_unchecked(val) }
105            }
106        };
107        Ok(res)
108    }
109}
110
111/// A thread-safe cell which can be written to only once.
112#[derive(Default, Debug)]
113pub struct OnceBool {
114    inner: OnceNonZeroUsize,
115}
116
117impl OnceBool {
118    /// Creates a new empty cell.
119    #[inline]
120    pub const fn new() -> OnceBool {
121        OnceBool { inner: OnceNonZeroUsize::new() }
122    }
123
124    /// Gets the underlying value.
125    #[inline]
126    pub fn get(&self) -> Option<bool> {
127        self.inner.get().map(OnceBool::from_usize)
128    }
129
130    /// Sets the contents of this cell to `value`.
131    ///
132    /// Returns `Ok(())` if the cell was empty and `Err(())` if it was
133    /// full.
134    #[inline]
135    pub fn set(&self, value: bool) -> Result<(), ()> {
136        self.inner.set(OnceBool::to_usize(value))
137    }
138
139    /// Gets the contents of the cell, initializing it with `f` if the cell was
140    /// empty.
141    ///
142    /// If several threads concurrently run `get_or_init`, more than one `f` can
143    /// be called. However, all threads will return the same value, produced by
144    /// some `f`.
145    pub fn get_or_init<F>(&self, f: F) -> bool
146    where
147        F: FnOnce() -> bool,
148    {
149        OnceBool::from_usize(self.inner.get_or_init(|| OnceBool::to_usize(f())))
150    }
151
152    /// Gets the contents of the cell, initializing it with `f` if
153    /// the cell was empty. If the cell was empty and `f` failed, an
154    /// error is returned.
155    ///
156    /// If several threads concurrently run `get_or_init`, more than one `f` can
157    /// be called. However, all threads will return the same value, produced by
158    /// some `f`.
159    pub fn get_or_try_init<F, E>(&self, f: F) -> Result<bool, E>
160    where
161        F: FnOnce() -> Result<bool, E>,
162    {
163        self.inner.get_or_try_init(|| f().map(OnceBool::to_usize)).map(OnceBool::from_usize)
164    }
165
166    #[inline]
167    fn from_usize(value: NonZeroUsize) -> bool {
168        value.get() == 1
169    }
170
171    #[inline]
172    fn to_usize(value: bool) -> NonZeroUsize {
173        unsafe { NonZeroUsize::new_unchecked(if value { 1 } else { 2 }) }
174    }
175}
176
177/// A thread-safe cell which can be written to only once.
178pub struct OnceRef<'a, T> {
179    inner: OnceNonZeroUsize,
180    ghost: PhantomData<UnsafeCell<&'a T>>,
181}
182
183// TODO: Replace UnsafeCell with SyncUnsafeCell once stabilized
184unsafe impl<'a, T: Sync> Sync for OnceRef<'a, T> {}
185
186impl<'a, T> core::fmt::Debug for OnceRef<'a, T> {
187    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
188        write!(f, "OnceRef({:?})", self.inner)
189    }
190}
191
192impl<'a, T> Default for OnceRef<'a, T> {
193    fn default() -> Self {
194        Self::new()
195    }
196}
197
198impl<'a, T> OnceRef<'a, T> {
199    /// Creates a new empty cell.
200    pub const fn new() -> OnceRef<'a, T> {
201        OnceRef { inner: OnceNonZeroUsize::new(), ghost: PhantomData }
202    }
203
204    /// Gets a reference to the underlying value.
205    pub fn get(&self) -> Option<&'a T> {
206        self.inner.get().map(|ptr| unsafe { &*(ptr.get() as *const T) })
207    }
208
209    /// Sets the contents of this cell to `value`.
210    ///
211    /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was
212    /// full.
213    pub fn set(&self, value: &'a T) -> Result<(), ()> {
214        let ptr = NonZeroUsize::new(value as *const T as usize).unwrap();
215        self.inner.set(ptr)
216    }
217
218    /// Gets the contents of the cell, initializing it with `f` if the cell was
219    /// empty.
220    ///
221    /// If several threads concurrently run `get_or_init`, more than one `f` can
222    /// be called. However, all threads will return the same value, produced by
223    /// some `f`.
224    pub fn get_or_init<F>(&self, f: F) -> &'a T
225    where
226        F: FnOnce() -> &'a T,
227    {
228        let f = || NonZeroUsize::new(f() as *const T as usize).unwrap();
229        let ptr = self.inner.get_or_init(f);
230        unsafe { &*(ptr.get() as *const T) }
231    }
232
233    /// Gets the contents of the cell, initializing it with `f` if
234    /// the cell was empty. If the cell was empty and `f` failed, an
235    /// error is returned.
236    ///
237    /// If several threads concurrently run `get_or_init`, more than one `f` can
238    /// be called. However, all threads will return the same value, produced by
239    /// some `f`.
240    pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&'a T, E>
241    where
242        F: FnOnce() -> Result<&'a T, E>,
243    {
244        let f = || f().map(|value| NonZeroUsize::new(value as *const T as usize).unwrap());
245        let ptr = self.inner.get_or_try_init(f)?;
246        unsafe { Ok(&*(ptr.get() as *const T)) }
247    }
248
249    /// ```compile_fail
250    /// use once_cell::race::OnceRef;
251    ///
252    /// let mut l = OnceRef::new();
253    ///
254    /// {
255    ///     let y = 2;
256    ///     let mut r = OnceRef::new();
257    ///     r.set(&y).unwrap();
258    ///     core::mem::swap(&mut l, &mut r);
259    /// }
260    ///
261    /// // l now contains a dangling reference to y
262    /// eprintln!("uaf: {}", l.get().unwrap());
263    /// ```
264    fn _dummy() {}
265}
266
267#[cfg(feature = "alloc")]
268pub use self::once_box::OnceBox;
269
270#[cfg(feature = "alloc")]
271mod once_box {
272    use super::atomic::{AtomicPtr, Ordering};
273    use core::{marker::PhantomData, ptr};
274
275    use alloc::boxed::Box;
276
277    /// A thread-safe cell which can be written to only once.
278    pub struct OnceBox<T> {
279        inner: AtomicPtr<T>,
280        ghost: PhantomData<Option<Box<T>>>,
281    }
282
283    impl<T> core::fmt::Debug for OnceBox<T> {
284        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
285            write!(f, "OnceBox({:?})", self.inner.load(Ordering::Relaxed))
286        }
287    }
288
289    impl<T> Default for OnceBox<T> {
290        fn default() -> Self {
291            Self::new()
292        }
293    }
294
295    impl<T> Drop for OnceBox<T> {
296        fn drop(&mut self) {
297            let ptr = *self.inner.get_mut();
298            if !ptr.is_null() {
299                drop(unsafe { Box::from_raw(ptr) })
300            }
301        }
302    }
303
304    impl<T> OnceBox<T> {
305        /// Creates a new empty cell.
306        pub const fn new() -> OnceBox<T> {
307            OnceBox { inner: AtomicPtr::new(ptr::null_mut()), ghost: PhantomData }
308        }
309
310        /// Gets a reference to the underlying value.
311        pub fn get(&self) -> Option<&T> {
312            let ptr = self.inner.load(Ordering::Acquire);
313            if ptr.is_null() {
314                return None;
315            }
316            Some(unsafe { &*ptr })
317        }
318
319        /// Sets the contents of this cell to `value`.
320        ///
321        /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was
322        /// full.
323        pub fn set(&self, value: Box<T>) -> Result<(), Box<T>> {
324            let ptr = Box::into_raw(value);
325            let exchange = self.inner.compare_exchange(
326                ptr::null_mut(),
327                ptr,
328                Ordering::AcqRel,
329                Ordering::Acquire,
330            );
331            if let Err(_) = exchange {
332                let value = unsafe { Box::from_raw(ptr) };
333                return Err(value);
334            }
335            Ok(())
336        }
337
338        /// Gets the contents of the cell, initializing it with `f` if the cell was
339        /// empty.
340        ///
341        /// If several threads concurrently run `get_or_init`, more than one `f` can
342        /// be called. However, all threads will return the same value, produced by
343        /// some `f`.
344        pub fn get_or_init<F>(&self, f: F) -> &T
345        where
346            F: FnOnce() -> Box<T>,
347        {
348            enum Void {}
349            match self.get_or_try_init(|| Ok::<Box<T>, Void>(f())) {
350                Ok(val) => val,
351                Err(void) => match void {},
352            }
353        }
354
355        /// Gets the contents of the cell, initializing it with `f` if
356        /// the cell was empty. If the cell was empty and `f` failed, an
357        /// error is returned.
358        ///
359        /// If several threads concurrently run `get_or_init`, more than one `f` can
360        /// be called. However, all threads will return the same value, produced by
361        /// some `f`.
362        pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
363        where
364            F: FnOnce() -> Result<Box<T>, E>,
365        {
366            let mut ptr = self.inner.load(Ordering::Acquire);
367
368            if ptr.is_null() {
369                let val = f()?;
370                ptr = Box::into_raw(val);
371                let exchange = self.inner.compare_exchange(
372                    ptr::null_mut(),
373                    ptr,
374                    Ordering::AcqRel,
375                    Ordering::Acquire,
376                );
377                if let Err(old) = exchange {
378                    drop(unsafe { Box::from_raw(ptr) });
379                    ptr = old;
380                }
381            };
382            Ok(unsafe { &*ptr })
383        }
384    }
385
386    unsafe impl<T: Sync + Send> Sync for OnceBox<T> {}
387
388    /// ```compile_fail
389    /// struct S(*mut ());
390    /// unsafe impl Sync for S {}
391    ///
392    /// fn share<T: Sync>(_: &T) {}
393    /// share(&once_cell::race::OnceBox::<S>::new());
394    /// ```
395    fn _dummy() {}
396}
397