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