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::AtomicPtr; |
12 | use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; |
13 | |
14 | pub(crate) struct OnceBox<T> { |
15 | ptr: AtomicPtr<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 | |