|
| 1 | +use crate::cell::{Cell, UnsafeCell}; |
| 2 | +use crate::ptr::{self, drop_in_place}; |
| 3 | +use crate::sys::thread_local::abort_on_dtor_unwind; |
| 4 | +use crate::sys::thread_local_dtor::register_dtor; |
| 5 | + |
| 6 | +#[derive(Clone, Copy)] |
| 7 | +enum State { |
| 8 | + Initial, |
| 9 | + Alive, |
| 10 | + Destroyed, |
| 11 | +} |
| 12 | + |
| 13 | +#[allow(missing_debug_implementations)] |
| 14 | +pub struct Storage<T> { |
| 15 | + state: Cell<State>, |
| 16 | + val: UnsafeCell<T>, |
| 17 | +} |
| 18 | + |
| 19 | +impl<T> Storage<T> { |
| 20 | + pub const fn new(val: T) -> Storage<T> { |
| 21 | + Storage { state: Cell::new(State::Initial), val: UnsafeCell::new(val) } |
| 22 | + } |
| 23 | + |
| 24 | + /// Get a reference to the TLS value. If the TLS variable has been destroyed, |
| 25 | + /// `None` is returned. |
| 26 | + /// |
| 27 | + /// # Safety |
| 28 | + /// * The `self` reference must remain valid until the TLS destructor has been |
| 29 | + /// run. |
| 30 | + /// * The returned reference may only be used until thread destruction occurs |
| 31 | + /// and may not be used after reentrant initialization has occurred. |
| 32 | + /// |
| 33 | + // FIXME(#110897): return NonNull instead of lying about the lifetime. |
| 34 | + #[inline] |
| 35 | + pub unsafe fn get(&self) -> Option<&'static T> { |
| 36 | + match self.state.get() { |
| 37 | + // SAFETY: as the state is not `Destroyed`, the value cannot have |
| 38 | + // been destroyed yet. The reference fulfills the terms outlined |
| 39 | + // above. |
| 40 | + State::Alive => unsafe { Some(&*self.val.get()) }, |
| 41 | + State::Destroyed => None, |
| 42 | + State::Initial => unsafe { self.initialize() }, |
| 43 | + } |
| 44 | + } |
| 45 | + |
| 46 | + #[cold] |
| 47 | + unsafe fn initialize(&self) -> Option<&'static T> { |
| 48 | + // Register the destructor |
| 49 | + |
| 50 | + // SAFETY: |
| 51 | + // * the destructor will be called at thread destruction. |
| 52 | + // * the caller guarantees that `self` will be valid until that time. |
| 53 | + unsafe { |
| 54 | + register_dtor(ptr::from_ref(self).cast_mut().cast(), destroy::<T>); |
| 55 | + } |
| 56 | + self.state.set(State::Alive); |
| 57 | + // SAFETY: as the state is not `Destroyed`, the value cannot have |
| 58 | + // been destroyed yet. The reference fulfills the terms outlined |
| 59 | + // above. |
| 60 | + unsafe { Some(&*self.val.get()) } |
| 61 | + } |
| 62 | +} |
| 63 | + |
| 64 | +/// Transition an `Alive` TLS variable into the `Destroyed` state, dropping its |
| 65 | +/// value. |
| 66 | +/// |
| 67 | +/// # Safety |
| 68 | +/// * Must only be called at thread destruction. |
| 69 | +/// * `ptr` must point to an instance of `Storage` with `Alive` state and be |
| 70 | +/// valid for accessing that instance. |
| 71 | +unsafe extern "C" fn destroy<T>(ptr: *mut u8) { |
| 72 | + // Print a nice abort message if a panic occurs. |
| 73 | + abort_on_dtor_unwind(|| { |
| 74 | + let storage = unsafe { &*(ptr as *const Storage<T>) }; |
| 75 | + // Update the state before running the destructor as it may attempt to |
| 76 | + // access the variable. |
| 77 | + storage.state.set(State::Destroyed); |
| 78 | + unsafe { |
| 79 | + drop_in_place(storage.val.get()); |
| 80 | + } |
| 81 | + }) |
| 82 | +} |
0 commit comments