]> git.proxmox.com Git - rustc.git/blame - vendor/once_cell/src/race.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / vendor / once_cell / src / race.rs
CommitLineData
136023e0 1//! Thread-safe, non-blocking, "first one wins" flavor of `OnceCell`.
5869c6ff
XL
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.
923072b8
FG
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`.
5869c6ff 21
a2a8927a
XL
22#[cfg(feature = "atomic-polyfill")]
23use atomic_polyfill as atomic;
24#[cfg(not(feature = "atomic-polyfill"))]
25use core::sync::atomic;
26
27use atomic::{AtomicUsize, Ordering};
28use core::num::NonZeroUsize;
5869c6ff
XL
29
30/// A thread-safe cell which can be written to only once.
31#[derive(Default, Debug)]
32pub struct OnceNonZeroUsize {
33 inner: AtomicUsize,
34}
35
36impl OnceNonZeroUsize {
37 /// Creates a new empty cell.
6a06907d 38 #[inline]
5869c6ff
XL
39 pub const fn new() -> OnceNonZeroUsize {
40 OnceNonZeroUsize { inner: AtomicUsize::new(0) }
41 }
42
43 /// Gets the underlying value.
6a06907d 44 #[inline]
5869c6ff
XL
45 pub fn get(&self) -> Option<NonZeroUsize> {
46 let val = self.inner.load(Ordering::Acquire);
47 NonZeroUsize::new(val)
48 }
49
50 /// Sets the contents of this cell to `value`.
51 ///
52 /// Returns `Ok(())` if the cell was empty and `Err(())` if it was
53 /// full.
6a06907d 54 #[inline]
5869c6ff 55 pub fn set(&self, value: NonZeroUsize) -> Result<(), ()> {
6a06907d
XL
56 let exchange =
57 self.inner.compare_exchange(0, value.get(), Ordering::AcqRel, Ordering::Acquire);
58 match exchange {
59 Ok(_) => Ok(()),
60 Err(_) => Err(()),
5869c6ff
XL
61 }
62 }
63
64 /// Gets the contents of the cell, initializing it with `f` if the cell was
65 /// empty.
66 ///
67 /// If several threads concurrently run `get_or_init`, more than one `f` can
68 /// be called. However, all threads will return the same value, produced by
69 /// some `f`.
70 pub fn get_or_init<F>(&self, f: F) -> NonZeroUsize
71 where
72 F: FnOnce() -> NonZeroUsize,
73 {
74 enum Void {}
75 match self.get_or_try_init(|| Ok::<NonZeroUsize, Void>(f())) {
76 Ok(val) => val,
77 Err(void) => match void {},
78 }
79 }
80
81 /// Gets the contents of the cell, initializing it with `f` if
82 /// the cell was empty. If the cell was empty and `f` failed, an
83 /// error is returned.
84 ///
85 /// If several threads concurrently run `get_or_init`, more than one `f` can
86 /// be called. However, all threads will return the same value, produced by
87 /// some `f`.
88 pub fn get_or_try_init<F, E>(&self, f: F) -> Result<NonZeroUsize, E>
89 where
90 F: FnOnce() -> Result<NonZeroUsize, E>,
91 {
92 let val = self.inner.load(Ordering::Acquire);
93 let res = match NonZeroUsize::new(val) {
94 Some(it) => it,
95 None => {
96 let mut val = f()?.get();
6a06907d
XL
97 let exchange =
98 self.inner.compare_exchange(0, val, Ordering::AcqRel, Ordering::Acquire);
99 if let Err(old) = exchange {
100 val = old;
5869c6ff
XL
101 }
102 unsafe { NonZeroUsize::new_unchecked(val) }
103 }
104 };
105 Ok(res)
106 }
107}
108
109/// A thread-safe cell which can be written to only once.
110#[derive(Default, Debug)]
111pub struct OnceBool {
112 inner: OnceNonZeroUsize,
113}
114
115impl OnceBool {
116 /// Creates a new empty cell.
6a06907d 117 #[inline]
5869c6ff
XL
118 pub const fn new() -> OnceBool {
119 OnceBool { inner: OnceNonZeroUsize::new() }
120 }
121
122 /// Gets the underlying value.
6a06907d 123 #[inline]
5869c6ff
XL
124 pub fn get(&self) -> Option<bool> {
125 self.inner.get().map(OnceBool::from_usize)
126 }
127
128 /// Sets the contents of this cell to `value`.
129 ///
130 /// Returns `Ok(())` if the cell was empty and `Err(())` if it was
131 /// full.
6a06907d 132 #[inline]
5869c6ff
XL
133 pub fn set(&self, value: bool) -> Result<(), ()> {
134 self.inner.set(OnceBool::to_usize(value))
135 }
136
137 /// Gets the contents of the cell, initializing it with `f` if the cell was
138 /// empty.
139 ///
140 /// If several threads concurrently run `get_or_init`, more than one `f` can
141 /// be called. However, all threads will return the same value, produced by
142 /// some `f`.
143 pub fn get_or_init<F>(&self, f: F) -> bool
144 where
145 F: FnOnce() -> bool,
146 {
147 OnceBool::from_usize(self.inner.get_or_init(|| OnceBool::to_usize(f())))
148 }
149
150 /// Gets the contents of the cell, initializing it with `f` if
151 /// the cell was empty. If the cell was empty and `f` failed, an
152 /// error is returned.
153 ///
154 /// If several threads concurrently run `get_or_init`, more than one `f` can
155 /// be called. However, all threads will return the same value, produced by
156 /// some `f`.
157 pub fn get_or_try_init<F, E>(&self, f: F) -> Result<bool, E>
158 where
159 F: FnOnce() -> Result<bool, E>,
160 {
161 self.inner.get_or_try_init(|| f().map(OnceBool::to_usize)).map(OnceBool::from_usize)
162 }
163
6a06907d 164 #[inline]
5869c6ff
XL
165 fn from_usize(value: NonZeroUsize) -> bool {
166 value.get() == 1
167 }
6a06907d 168 #[inline]
5869c6ff
XL
169 fn to_usize(value: bool) -> NonZeroUsize {
170 unsafe { NonZeroUsize::new_unchecked(if value { 1 } else { 2 }) }
171 }
172}
173
174#[cfg(feature = "alloc")]
175pub use self::once_box::OnceBox;
6a06907d 176
5869c6ff
XL
177#[cfg(feature = "alloc")]
178mod once_box {
a2a8927a
XL
179 use super::atomic::{AtomicPtr, Ordering};
180 use core::{marker::PhantomData, ptr};
5869c6ff 181
6a06907d
XL
182 use alloc::boxed::Box;
183
5869c6ff 184 /// A thread-safe cell which can be written to only once.
5869c6ff
XL
185 pub struct OnceBox<T> {
186 inner: AtomicPtr<T>,
187 ghost: PhantomData<Option<Box<T>>>,
188 }
189
a2a8927a
XL
190 impl<T> core::fmt::Debug for OnceBox<T> {
191 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
192 write!(f, "OnceBox({:?})", self.inner.load(Ordering::Relaxed))
193 }
194 }
195
6a06907d
XL
196 impl<T> Default for OnceBox<T> {
197 fn default() -> Self {
198 Self::new()
199 }
200 }
201
5869c6ff
XL
202 impl<T> Drop for OnceBox<T> {
203 fn drop(&mut self) {
204 let ptr = *self.inner.get_mut();
205 if !ptr.is_null() {
206 drop(unsafe { Box::from_raw(ptr) })
207 }
208 }
209 }
210
211 impl<T> OnceBox<T> {
212 /// Creates a new empty cell.
213 pub const fn new() -> OnceBox<T> {
214 OnceBox { inner: AtomicPtr::new(ptr::null_mut()), ghost: PhantomData }
215 }
216
217 /// Gets a reference to the underlying value.
218 pub fn get(&self) -> Option<&T> {
219 let ptr = self.inner.load(Ordering::Acquire);
220 if ptr.is_null() {
221 return None;
222 }
223 Some(unsafe { &*ptr })
224 }
225
226 /// Sets the contents of this cell to `value`.
227 ///
228 /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was
229 /// full.
230 pub fn set(&self, value: Box<T>) -> Result<(), Box<T>> {
231 let ptr = Box::into_raw(value);
6a06907d
XL
232 let exchange = self.inner.compare_exchange(
233 ptr::null_mut(),
234 ptr,
235 Ordering::AcqRel,
236 Ordering::Acquire,
237 );
238 if let Err(_) = exchange {
5869c6ff
XL
239 let value = unsafe { Box::from_raw(ptr) };
240 return Err(value);
241 }
242 Ok(())
243 }
244
245 /// Gets the contents of the cell, initializing it with `f` if the cell was
246 /// empty.
247 ///
248 /// If several threads concurrently run `get_or_init`, more than one `f` can
249 /// be called. However, all threads will return the same value, produced by
250 /// some `f`.
251 pub fn get_or_init<F>(&self, f: F) -> &T
252 where
253 F: FnOnce() -> Box<T>,
254 {
255 enum Void {}
256 match self.get_or_try_init(|| Ok::<Box<T>, Void>(f())) {
257 Ok(val) => val,
258 Err(void) => match void {},
259 }
260 }
261
262 /// Gets the contents of the cell, initializing it with `f` if
263 /// the cell was empty. If the cell was empty and `f` failed, an
264 /// error is returned.
265 ///
266 /// If several threads concurrently run `get_or_init`, more than one `f` can
267 /// be called. However, all threads will return the same value, produced by
268 /// some `f`.
269 pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
270 where
271 F: FnOnce() -> Result<Box<T>, E>,
272 {
273 let mut ptr = self.inner.load(Ordering::Acquire);
274
275 if ptr.is_null() {
276 let val = f()?;
277 ptr = Box::into_raw(val);
6a06907d
XL
278 let exchange = self.inner.compare_exchange(
279 ptr::null_mut(),
280 ptr,
281 Ordering::AcqRel,
282 Ordering::Acquire,
283 );
284 if let Err(old) = exchange {
5869c6ff 285 drop(unsafe { Box::from_raw(ptr) });
6a06907d 286 ptr = old;
5869c6ff
XL
287 }
288 };
289 Ok(unsafe { &*ptr })
290 }
291 }
292
6a06907d
XL
293 unsafe impl<T: Sync + Send> Sync for OnceBox<T> {}
294
5869c6ff
XL
295 /// ```compile_fail
296 /// struct S(*mut ());
297 /// unsafe impl Sync for S {}
298 ///
299 /// fn share<T: Sync>(_: &T) {}
300 /// share(&once_cell::race::OnceBox::<S>::new());
301 /// ```
6a06907d 302 fn _dummy() {}
5869c6ff 303}