/rust/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.20.3/src/race.rs
Line | Count | Source (jump to first uncovered line) |
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(not(feature = "portable-atomic"))] |
23 | | use core::sync::atomic; |
24 | | #[cfg(feature = "portable-atomic")] |
25 | | use portable_atomic as atomic; |
26 | | |
27 | | use atomic::{AtomicPtr, AtomicUsize, Ordering}; |
28 | | use core::cell::UnsafeCell; |
29 | | use core::marker::PhantomData; |
30 | | use core::num::NonZeroUsize; |
31 | | use core::ptr; |
32 | | |
33 | | /// A thread-safe cell which can be written to only once. |
34 | | #[derive(Default, Debug)] |
35 | | pub struct OnceNonZeroUsize { |
36 | | inner: AtomicUsize, |
37 | | } |
38 | | |
39 | | impl OnceNonZeroUsize { |
40 | | /// Creates a new empty cell. |
41 | | #[inline] |
42 | 0 | pub const fn new() -> OnceNonZeroUsize { |
43 | 0 | OnceNonZeroUsize { inner: AtomicUsize::new(0) } |
44 | 0 | } |
45 | | |
46 | | /// Gets the underlying value. |
47 | | #[inline] |
48 | 0 | pub fn get(&self) -> Option<NonZeroUsize> { |
49 | 0 | let val = self.inner.load(Ordering::Acquire); |
50 | 0 | NonZeroUsize::new(val) |
51 | 0 | } |
52 | | |
53 | | /// Sets the contents of this cell to `value`. |
54 | | /// |
55 | | /// Returns `Ok(())` if the cell was empty and `Err(())` if it was |
56 | | /// full. |
57 | | #[inline] |
58 | 0 | pub fn set(&self, value: NonZeroUsize) -> Result<(), ()> { |
59 | 0 | let exchange = |
60 | 0 | self.inner.compare_exchange(0, value.get(), Ordering::AcqRel, Ordering::Acquire); |
61 | 0 | match exchange { |
62 | 0 | Ok(_) => Ok(()), |
63 | 0 | Err(_) => Err(()), |
64 | | } |
65 | 0 | } |
66 | | |
67 | | /// Gets the contents of the cell, initializing it with `f` if the cell was |
68 | | /// empty. |
69 | | /// |
70 | | /// If several threads concurrently run `get_or_init`, more than one `f` can |
71 | | /// be called. However, all threads will return the same value, produced by |
72 | | /// some `f`. |
73 | 0 | pub fn get_or_init<F>(&self, f: F) -> NonZeroUsize |
74 | 0 | where |
75 | 0 | F: FnOnce() -> NonZeroUsize, |
76 | 0 | { |
77 | | enum Void {} |
78 | 0 | match self.get_or_try_init(|| Ok::<NonZeroUsize, Void>(f())) { |
79 | 0 | Ok(val) => val, |
80 | 0 | Err(void) => match void {}, |
81 | 0 | } |
82 | 0 | } |
83 | | |
84 | | /// Gets the contents of the cell, initializing it with `f` if |
85 | | /// the cell was empty. If the cell was empty and `f` failed, an |
86 | | /// error is returned. |
87 | | /// |
88 | | /// If several threads concurrently run `get_or_init`, more than one `f` can |
89 | | /// be called. However, all threads will return the same value, produced by |
90 | | /// some `f`. |
91 | 0 | pub fn get_or_try_init<F, E>(&self, f: F) -> Result<NonZeroUsize, E> |
92 | 0 | where |
93 | 0 | F: FnOnce() -> Result<NonZeroUsize, E>, |
94 | 0 | { |
95 | 0 | let val = self.inner.load(Ordering::Acquire); |
96 | 0 | match NonZeroUsize::new(val) { |
97 | 0 | Some(it) => Ok(it), |
98 | 0 | None => self.init(f), |
99 | | } |
100 | 0 | } |
101 | | |
102 | | #[cold] |
103 | | #[inline(never)] |
104 | 0 | fn init<E>(&self, f: impl FnOnce() -> Result<NonZeroUsize, E>) -> Result<NonZeroUsize, E> { |
105 | 0 | let mut val = f()?.get(); |
106 | 0 | let exchange = self.inner.compare_exchange(0, val, Ordering::AcqRel, Ordering::Acquire); |
107 | 0 | if let Err(old) = exchange { |
108 | 0 | val = old; |
109 | 0 | } |
110 | 0 | Ok(unsafe { NonZeroUsize::new_unchecked(val) }) |
111 | 0 | } |
112 | | } |
113 | | |
114 | | /// A thread-safe cell which can be written to only once. |
115 | | #[derive(Default, Debug)] |
116 | | pub struct OnceBool { |
117 | | inner: OnceNonZeroUsize, |
118 | | } |
119 | | |
120 | | impl OnceBool { |
121 | | /// Creates a new empty cell. |
122 | | #[inline] |
123 | 0 | pub const fn new() -> OnceBool { |
124 | 0 | OnceBool { inner: OnceNonZeroUsize::new() } |
125 | 0 | } |
126 | | |
127 | | /// Gets the underlying value. |
128 | | #[inline] |
129 | 0 | pub fn get(&self) -> Option<bool> { |
130 | 0 | self.inner.get().map(OnceBool::from_usize) |
131 | 0 | } |
132 | | |
133 | | /// Sets the contents of this cell to `value`. |
134 | | /// |
135 | | /// Returns `Ok(())` if the cell was empty and `Err(())` if it was |
136 | | /// full. |
137 | | #[inline] |
138 | 0 | pub fn set(&self, value: bool) -> Result<(), ()> { |
139 | 0 | self.inner.set(OnceBool::to_usize(value)) |
140 | 0 | } |
141 | | |
142 | | /// Gets the contents of the cell, initializing it with `f` if the cell was |
143 | | /// empty. |
144 | | /// |
145 | | /// If several threads concurrently run `get_or_init`, more than one `f` can |
146 | | /// be called. However, all threads will return the same value, produced by |
147 | | /// some `f`. |
148 | 0 | pub fn get_or_init<F>(&self, f: F) -> bool |
149 | 0 | where |
150 | 0 | F: FnOnce() -> bool, |
151 | 0 | { |
152 | 0 | OnceBool::from_usize(self.inner.get_or_init(|| OnceBool::to_usize(f()))) |
153 | 0 | } |
154 | | |
155 | | /// Gets the contents of the cell, initializing it with `f` if |
156 | | /// the cell was empty. If the cell was empty and `f` failed, an |
157 | | /// error is returned. |
158 | | /// |
159 | | /// If several threads concurrently run `get_or_init`, more than one `f` can |
160 | | /// be called. However, all threads will return the same value, produced by |
161 | | /// some `f`. |
162 | 0 | pub fn get_or_try_init<F, E>(&self, f: F) -> Result<bool, E> |
163 | 0 | where |
164 | 0 | F: FnOnce() -> Result<bool, E>, |
165 | 0 | { |
166 | 0 | self.inner.get_or_try_init(|| f().map(OnceBool::to_usize)).map(OnceBool::from_usize) |
167 | 0 | } |
168 | | |
169 | | #[inline] |
170 | 0 | fn from_usize(value: NonZeroUsize) -> bool { |
171 | 0 | value.get() == 1 |
172 | 0 | } |
173 | | |
174 | | #[inline] |
175 | 0 | fn to_usize(value: bool) -> NonZeroUsize { |
176 | 0 | unsafe { NonZeroUsize::new_unchecked(if value { 1 } else { 2 }) } |
177 | 0 | } |
178 | | } |
179 | | |
180 | | /// A thread-safe cell which can be written to only once. |
181 | | pub struct OnceRef<'a, T> { |
182 | | inner: AtomicPtr<T>, |
183 | | ghost: PhantomData<UnsafeCell<&'a T>>, |
184 | | } |
185 | | |
186 | | // TODO: Replace UnsafeCell with SyncUnsafeCell once stabilized |
187 | | unsafe impl<'a, T: Sync> Sync for OnceRef<'a, T> {} |
188 | | |
189 | | impl<'a, T> core::fmt::Debug for OnceRef<'a, T> { |
190 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
191 | 0 | write!(f, "OnceRef({:?})", self.inner) |
192 | 0 | } |
193 | | } |
194 | | |
195 | | impl<'a, T> Default for OnceRef<'a, T> { |
196 | 0 | fn default() -> Self { |
197 | 0 | Self::new() |
198 | 0 | } |
199 | | } |
200 | | |
201 | | impl<'a, T> OnceRef<'a, T> { |
202 | | /// Creates a new empty cell. |
203 | 0 | pub const fn new() -> OnceRef<'a, T> { |
204 | 0 | OnceRef { inner: AtomicPtr::new(ptr::null_mut()), ghost: PhantomData } |
205 | 0 | } |
206 | | |
207 | | /// Gets a reference to the underlying value. |
208 | 0 | pub fn get(&self) -> Option<&'a T> { |
209 | 0 | let ptr = self.inner.load(Ordering::Acquire); |
210 | 0 | unsafe { ptr.as_ref() } |
211 | 0 | } |
212 | | |
213 | | /// Sets the contents of this cell to `value`. |
214 | | /// |
215 | | /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was |
216 | | /// full. |
217 | 0 | pub fn set(&self, value: &'a T) -> Result<(), ()> { |
218 | 0 | let ptr = value as *const T as *mut T; |
219 | 0 | let exchange = |
220 | 0 | self.inner.compare_exchange(ptr::null_mut(), ptr, Ordering::AcqRel, Ordering::Acquire); |
221 | 0 | match exchange { |
222 | 0 | Ok(_) => Ok(()), |
223 | 0 | Err(_) => Err(()), |
224 | | } |
225 | 0 | } |
226 | | |
227 | | /// Gets the contents of the cell, initializing it with `f` if the cell was |
228 | | /// empty. |
229 | | /// |
230 | | /// If several threads concurrently run `get_or_init`, more than one `f` can |
231 | | /// be called. However, all threads will return the same value, produced by |
232 | | /// some `f`. |
233 | 0 | pub fn get_or_init<F>(&self, f: F) -> &'a T |
234 | 0 | where |
235 | 0 | F: FnOnce() -> &'a T, |
236 | 0 | { |
237 | | enum Void {} |
238 | 0 | match self.get_or_try_init(|| Ok::<&'a T, Void>(f())) { |
239 | 0 | Ok(val) => val, |
240 | 0 | Err(void) => match void {}, |
241 | 0 | } |
242 | 0 | } |
243 | | |
244 | | /// Gets the contents of the cell, initializing it with `f` if |
245 | | /// the cell was empty. If the cell was empty and `f` failed, an |
246 | | /// error is returned. |
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 | 0 | pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&'a T, E> |
252 | 0 | where |
253 | 0 | F: FnOnce() -> Result<&'a T, E>, |
254 | 0 | { |
255 | 0 | let mut ptr = self.inner.load(Ordering::Acquire); |
256 | 0 |
|
257 | 0 | if ptr.is_null() { |
258 | | // TODO replace with `cast_mut` when MSRV reaches 1.65.0 (also in `set`) |
259 | 0 | ptr = f()? as *const T as *mut T; |
260 | 0 | let exchange = self.inner.compare_exchange( |
261 | 0 | ptr::null_mut(), |
262 | 0 | ptr, |
263 | 0 | Ordering::AcqRel, |
264 | 0 | Ordering::Acquire, |
265 | 0 | ); |
266 | 0 | if let Err(old) = exchange { |
267 | 0 | ptr = old; |
268 | 0 | } |
269 | 0 | } |
270 | | |
271 | 0 | Ok(unsafe { &*ptr }) |
272 | 0 | } |
273 | | |
274 | | /// ```compile_fail |
275 | | /// use once_cell::race::OnceRef; |
276 | | /// |
277 | | /// let mut l = OnceRef::new(); |
278 | | /// |
279 | | /// { |
280 | | /// let y = 2; |
281 | | /// let mut r = OnceRef::new(); |
282 | | /// r.set(&y).unwrap(); |
283 | | /// core::mem::swap(&mut l, &mut r); |
284 | | /// } |
285 | | /// |
286 | | /// // l now contains a dangling reference to y |
287 | | /// eprintln!("uaf: {}", l.get().unwrap()); |
288 | | /// ``` |
289 | 0 | fn _dummy() {} |
290 | | } |
291 | | |
292 | | #[cfg(feature = "alloc")] |
293 | | pub use self::once_box::OnceBox; |
294 | | |
295 | | #[cfg(feature = "alloc")] |
296 | | mod once_box { |
297 | | use super::atomic::{AtomicPtr, Ordering}; |
298 | | use core::{marker::PhantomData, ptr}; |
299 | | |
300 | | use alloc::boxed::Box; |
301 | | |
302 | | /// A thread-safe cell which can be written to only once. |
303 | | pub struct OnceBox<T> { |
304 | | inner: AtomicPtr<T>, |
305 | | ghost: PhantomData<Option<Box<T>>>, |
306 | | } |
307 | | |
308 | | impl<T> core::fmt::Debug for OnceBox<T> { |
309 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
310 | 0 | write!(f, "OnceBox({:?})", self.inner.load(Ordering::Relaxed)) |
311 | 0 | } |
312 | | } |
313 | | |
314 | | impl<T> Default for OnceBox<T> { |
315 | 0 | fn default() -> Self { |
316 | 0 | Self::new() |
317 | 0 | } |
318 | | } |
319 | | |
320 | | impl<T> Drop for OnceBox<T> { |
321 | 0 | fn drop(&mut self) { |
322 | 0 | let ptr = *self.inner.get_mut(); |
323 | 0 | if !ptr.is_null() { |
324 | 0 | drop(unsafe { Box::from_raw(ptr) }) |
325 | 0 | } |
326 | 0 | } |
327 | | } |
328 | | |
329 | | impl<T> OnceBox<T> { |
330 | | /// Creates a new empty cell. |
331 | 0 | pub const fn new() -> OnceBox<T> { |
332 | 0 | OnceBox { inner: AtomicPtr::new(ptr::null_mut()), ghost: PhantomData } |
333 | 0 | } |
334 | | |
335 | | /// Gets a reference to the underlying value. |
336 | 0 | pub fn get(&self) -> Option<&T> { |
337 | 0 | let ptr = self.inner.load(Ordering::Acquire); |
338 | 0 | if ptr.is_null() { |
339 | 0 | return None; |
340 | 0 | } |
341 | 0 | Some(unsafe { &*ptr }) |
342 | 0 | } |
343 | | |
344 | | /// Sets the contents of this cell to `value`. |
345 | | /// |
346 | | /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was |
347 | | /// full. |
348 | 0 | pub fn set(&self, value: Box<T>) -> Result<(), Box<T>> { |
349 | 0 | let ptr = Box::into_raw(value); |
350 | 0 | let exchange = self.inner.compare_exchange( |
351 | 0 | ptr::null_mut(), |
352 | 0 | ptr, |
353 | 0 | Ordering::AcqRel, |
354 | 0 | Ordering::Acquire, |
355 | 0 | ); |
356 | 0 | if exchange.is_err() { |
357 | 0 | let value = unsafe { Box::from_raw(ptr) }; |
358 | 0 | return Err(value); |
359 | 0 | } |
360 | 0 | Ok(()) |
361 | 0 | } |
362 | | |
363 | | /// Gets the contents of the cell, initializing it with `f` if the cell was |
364 | | /// empty. |
365 | | /// |
366 | | /// If several threads concurrently run `get_or_init`, more than one `f` can |
367 | | /// be called. However, all threads will return the same value, produced by |
368 | | /// some `f`. |
369 | 0 | pub fn get_or_init<F>(&self, f: F) -> &T |
370 | 0 | where |
371 | 0 | F: FnOnce() -> Box<T>, |
372 | 0 | { |
373 | | enum Void {} |
374 | 0 | match self.get_or_try_init(|| Ok::<Box<T>, Void>(f())) { |
375 | 0 | Ok(val) => val, |
376 | 0 | Err(void) => match void {}, |
377 | 0 | } |
378 | 0 | } |
379 | | |
380 | | /// Gets the contents of the cell, initializing it with `f` if |
381 | | /// the cell was empty. If the cell was empty and `f` failed, an |
382 | | /// error is returned. |
383 | | /// |
384 | | /// If several threads concurrently run `get_or_init`, more than one `f` can |
385 | | /// be called. However, all threads will return the same value, produced by |
386 | | /// some `f`. |
387 | 0 | pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E> |
388 | 0 | where |
389 | 0 | F: FnOnce() -> Result<Box<T>, E>, |
390 | 0 | { |
391 | 0 | let mut ptr = self.inner.load(Ordering::Acquire); |
392 | 0 |
|
393 | 0 | if ptr.is_null() { |
394 | 0 | let val = f()?; |
395 | 0 | ptr = Box::into_raw(val); |
396 | 0 | let exchange = self.inner.compare_exchange( |
397 | 0 | ptr::null_mut(), |
398 | 0 | ptr, |
399 | 0 | Ordering::AcqRel, |
400 | 0 | Ordering::Acquire, |
401 | 0 | ); |
402 | 0 | if let Err(old) = exchange { |
403 | 0 | drop(unsafe { Box::from_raw(ptr) }); |
404 | 0 | ptr = old; |
405 | 0 | } |
406 | 0 | }; |
407 | 0 | Ok(unsafe { &*ptr }) |
408 | 0 | } |
409 | | } |
410 | | |
411 | | unsafe impl<T: Sync + Send> Sync for OnceBox<T> {} |
412 | | |
413 | | /// ```compile_fail |
414 | | /// struct S(*mut ()); |
415 | | /// unsafe impl Sync for S {} |
416 | | /// |
417 | | /// fn share<T: Sync>(_: &T) {} |
418 | | /// share(&once_cell::race::OnceBox::<S>::new()); |
419 | | /// ``` |
420 | 0 | fn _dummy() {} |
421 | | } |