| 1 | use crate::cell::{Cell, UnsafeCell}; |
| 2 | use crate::mem::MaybeUninit; |
| 3 | use crate::ptr; |
| 4 | use crate::sys::thread_local::{abort_on_dtor_unwind, destructors}; |
| 5 | |
| 6 | pub unsafe trait DestroyedState: Sized + Copy { |
| 7 | fn register_dtor<T>(s: &Storage<T, Self>); |
| 8 | } |
| 9 | |
| 10 | unsafe impl DestroyedState for ! { |
| 11 | fn register_dtor<T>(_: &Storage<T, !>) {} |
| 12 | } |
| 13 | |
| 14 | unsafe impl DestroyedState for () { |
| 15 | fn register_dtor<T>(s: &Storage<T, ()>) { |
| 16 | unsafe { |
| 17 | destructors::register(t:ptr::from_ref(s).cast_mut().cast(), dtor:destroy::<T>); |
| 18 | } |
| 19 | } |
| 20 | } |
| 21 | |
| 22 | #[derive (Copy, Clone)] |
| 23 | enum State<D> { |
| 24 | Uninitialized, |
| 25 | Alive, |
| 26 | Destroyed(D), |
| 27 | } |
| 28 | |
| 29 | #[allow (missing_debug_implementations)] |
| 30 | pub struct Storage<T, D> { |
| 31 | state: Cell<State<D>>, |
| 32 | value: UnsafeCell<MaybeUninit<T>>, |
| 33 | } |
| 34 | |
| 35 | impl<T, D> Storage<T, D> |
| 36 | where |
| 37 | D: DestroyedState, |
| 38 | { |
| 39 | pub const fn new() -> Storage<T, D> { |
| 40 | Storage { |
| 41 | state: Cell::new(State::Uninitialized), |
| 42 | value: UnsafeCell::new(MaybeUninit::uninit()), |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | /// Gets a pointer to the TLS value, potentially initializing it with the |
| 47 | /// provided parameters. If the TLS variable has been destroyed, a null |
| 48 | /// pointer is returned. |
| 49 | /// |
| 50 | /// The resulting pointer may not be used after reentrant inialialization |
| 51 | /// or thread destruction has occurred. |
| 52 | /// |
| 53 | /// # Safety |
| 54 | /// The `self` reference must remain valid until the TLS destructor is run. |
| 55 | #[inline ] |
| 56 | pub unsafe fn get_or_init(&self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T { |
| 57 | if let State::Alive = self.state.get() { |
| 58 | self.value.get().cast() |
| 59 | } else { |
| 60 | unsafe { self.get_or_init_slow(i, f) } |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | /// # Safety |
| 65 | /// The `self` reference must remain valid until the TLS destructor is run. |
| 66 | #[cold ] |
| 67 | unsafe fn get_or_init_slow( |
| 68 | &self, |
| 69 | i: Option<&mut Option<T>>, |
| 70 | f: impl FnOnce() -> T, |
| 71 | ) -> *const T { |
| 72 | match self.state.get() { |
| 73 | State::Uninitialized => {} |
| 74 | State::Alive => return self.value.get().cast(), |
| 75 | State::Destroyed(_) => return ptr::null(), |
| 76 | } |
| 77 | |
| 78 | let v = i.and_then(Option::take).unwrap_or_else(f); |
| 79 | |
| 80 | // SAFETY: we cannot be inside a `LocalKey::with` scope, as the initializer |
| 81 | // has already returned and the next scope only starts after we return |
| 82 | // the pointer. Therefore, there can be no references to the old value, |
| 83 | // even if it was initialized. Thus because we are !Sync we have exclusive |
| 84 | // access to self.value and may replace it. |
| 85 | let mut old_value = unsafe { self.value.get().replace(MaybeUninit::new(v)) }; |
| 86 | match self.state.replace(State::Alive) { |
| 87 | // If the variable is not being recursively initialized, register |
| 88 | // the destructor. This might be a noop if the value does not need |
| 89 | // destruction. |
| 90 | State::Uninitialized => D::register_dtor(self), |
| 91 | |
| 92 | // Recursive initialization, we only need to drop the old value |
| 93 | // as we've already registered the destructor. |
| 94 | State::Alive => unsafe { old_value.assume_init_drop() }, |
| 95 | |
| 96 | State::Destroyed(_) => unreachable!(), |
| 97 | } |
| 98 | |
| 99 | self.value.get().cast() |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | /// Transition an `Alive` TLS variable into the `Destroyed` state, dropping its |
| 104 | /// value. |
| 105 | /// |
| 106 | /// # Safety |
| 107 | /// * Must only be called at thread destruction. |
| 108 | /// * `ptr` must point to an instance of `Storage<T, ()>` and be valid for |
| 109 | /// accessing that instance. |
| 110 | unsafe extern "C" fn destroy<T>(ptr: *mut u8) { |
| 111 | // Print a nice abort message if a panic occurs. |
| 112 | abort_on_dtor_unwind(|| { |
| 113 | let storage: &Storage = unsafe { &*(ptr as *const Storage<T, ()>) }; |
| 114 | if let State::Alive = storage.state.replace(val:State::Destroyed(())) { |
| 115 | // SAFETY: we ensured the state was Alive so the value was initialized. |
| 116 | // We also updated the state to Destroyed to prevent the destructor |
| 117 | // from accessing the thread-local variable, as this would violate |
| 118 | // the exclusive access provided by &mut T in Drop::drop. |
| 119 | unsafe { (*storage.value.get()).assume_init_drop() } |
| 120 | } |
| 121 | }) |
| 122 | } |
| 123 | |