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}