| 1 | //! A racily-initialized alternative to `OnceLock<Box<T>>`. |
| 2 | //! |
| 3 | //! This is used to implement synchronization primitives that need allocation, |
| 4 | //! like the pthread versions. |
| 5 | |
| 6 | #![allow (dead_code)] // Only used on some platforms. |
| 7 | |
| 8 | use crate::mem::replace; |
| 9 | use crate::pin::Pin; |
| 10 | use crate::ptr::null_mut; |
| 11 | use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; |
| 12 | use crate::sync::atomic::{Atomic, AtomicPtr}; |
| 13 | |
| 14 | pub(crate) struct OnceBox<T> { |
| 15 | ptr: Atomic<*mut T>, |
| 16 | } |
| 17 | |
| 18 | impl<T> OnceBox<T> { |
| 19 | #[inline ] |
| 20 | pub const fn new() -> Self { |
| 21 | Self { ptr: AtomicPtr::new(null_mut()) } |
| 22 | } |
| 23 | |
| 24 | /// Gets access to the value, assuming it is already initialized and this |
| 25 | /// initialization has been observed by the current thread. |
| 26 | /// |
| 27 | /// Since all modifications to the pointer have already been observed, the |
| 28 | /// pointer load in this function can be performed with relaxed ordering, |
| 29 | /// potentially allowing the optimizer to turn code like this: |
| 30 | /// ```rust, ignore |
| 31 | /// once_box.get_or_init(|| Box::pin(42)); |
| 32 | /// unsafe { once_box.get_unchecked() } |
| 33 | /// ``` |
| 34 | /// into |
| 35 | /// ```rust, ignore |
| 36 | /// once_box.get_or_init(|| Box::pin(42)) |
| 37 | /// ``` |
| 38 | /// |
| 39 | /// # Safety |
| 40 | /// This causes undefined behavior if the assumption above is violated. |
| 41 | #[inline ] |
| 42 | pub unsafe fn get_unchecked(&self) -> Pin<&T> { |
| 43 | unsafe { Pin::new_unchecked(&*self.ptr.load(Relaxed)) } |
| 44 | } |
| 45 | |
| 46 | #[inline ] |
| 47 | pub fn get_or_init(&self, f: impl FnOnce() -> Pin<Box<T>>) -> Pin<&T> { |
| 48 | let ptr = self.ptr.load(Acquire); |
| 49 | match unsafe { ptr.as_ref() } { |
| 50 | Some(val) => unsafe { Pin::new_unchecked(val) }, |
| 51 | None => self.initialize(f), |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | #[inline ] |
| 56 | pub fn take(&mut self) -> Option<Pin<Box<T>>> { |
| 57 | let ptr = replace(self.ptr.get_mut(), null_mut()); |
| 58 | if !ptr.is_null() { Some(unsafe { Pin::new_unchecked(Box::from_raw(ptr)) }) } else { None } |
| 59 | } |
| 60 | |
| 61 | #[cold ] |
| 62 | fn initialize(&self, f: impl FnOnce() -> Pin<Box<T>>) -> Pin<&T> { |
| 63 | let new_ptr = Box::into_raw(unsafe { Pin::into_inner_unchecked(f()) }); |
| 64 | match self.ptr.compare_exchange(null_mut(), new_ptr, Release, Acquire) { |
| 65 | Ok(_) => unsafe { Pin::new_unchecked(&*new_ptr) }, |
| 66 | Err(ptr) => { |
| 67 | // Lost the race to another thread. |
| 68 | // Drop the value we created, and use the one from the other thread instead. |
| 69 | drop(unsafe { Box::from_raw(new_ptr) }); |
| 70 | unsafe { Pin::new_unchecked(&*ptr) } |
| 71 | } |
| 72 | } |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | unsafe impl<T: Send> Send for OnceBox<T> {} |
| 77 | unsafe impl<T: Send + Sync> Sync for OnceBox<T> {} |
| 78 | |
| 79 | impl<T> Drop for OnceBox<T> { |
| 80 | fn drop(&mut self) { |
| 81 | self.take(); |
| 82 | } |
| 83 | } |
| 84 | |