ring/polyfill/once_cell/
race.rs

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
22use core::sync::atomic;
23
24use atomic::{AtomicUsize, Ordering};
25use core::num::NonZeroUsize;
26
27/// A thread-safe cell which can be written to only once.
28pub struct OnceNonZeroUsize {
29    inner: AtomicUsize,
30}
31
32impl OnceNonZeroUsize {
33    /// Creates a new empty cell.
34    #[inline]
35    pub const fn new() -> OnceNonZeroUsize {
36        OnceNonZeroUsize {
37            inner: AtomicUsize::new(0),
38        }
39    }
40
41    /// Gets the underlying value.
42    #[inline]
43    pub fn get(&self) -> Option<NonZeroUsize> {
44        let val = self.inner.load(Ordering::Acquire);
45        NonZeroUsize::new(val)
46    }
47
48    /// Gets the contents of the cell, initializing it with `f` if the cell was
49    /// empty.
50    ///
51    /// If several threads concurrently run `get_or_init`, more than one `f` can
52    /// be called. However, all threads will return the same value, produced by
53    /// some `f`.
54    pub fn get_or_init<F>(&self, f: F) -> NonZeroUsize
55    where
56        F: FnOnce() -> NonZeroUsize,
57    {
58        let val = self.inner.load(Ordering::Acquire);
59        match NonZeroUsize::new(val) {
60            Some(it) => it,
61            None => self.init(f),
62        }
63    }
64
65    #[cold]
66    #[inline(never)]
67    fn init(&self, f: impl FnOnce() -> NonZeroUsize) -> NonZeroUsize {
68        let mut val = f().get();
69        let exchange = self
70            .inner
71            .compare_exchange(0, val, Ordering::AcqRel, Ordering::Acquire);
72        if let Err(old) = exchange {
73            val = old;
74        }
75        unsafe { NonZeroUsize::new_unchecked(val) }
76    }
77}