1 | //! Thread local support for platforms with native TLS. |
2 | //! |
3 | //! To achieve the best performance, we choose from four different types for |
4 | //! the TLS variable, depending on the method of initialization used (`const` |
5 | //! or lazy) and the drop requirements of the stored type: |
6 | //! |
7 | //! | | `Drop` | `!Drop` | |
8 | //! |--------:|:--------------------:|:-------------------:| |
9 | //! | `const` | `EagerStorage<T>` | `T` | |
10 | //! | lazy | `LazyStorage<T, ()>` | `LazyStorage<T, !>` | |
11 | //! |
12 | //! For `const` initialization and `!Drop` types, we simply use `T` directly, |
13 | //! but for other situations, we implement a state machine to handle |
14 | //! initialization of the variable and its destructor and destruction. |
15 | //! Upon accessing the TLS variable, the current state is compared: |
16 | //! |
17 | //! 1. If the state is `Initial`, initialize the storage, transition the state |
18 | //! to `Alive` and (if applicable) register the destructor, and return a |
19 | //! reference to the value. |
20 | //! 2. If the state is `Alive`, initialization was previously completed, so |
21 | //! return a reference to the value. |
22 | //! 3. If the state is `Destroyed`, the destructor has been run already, so |
23 | //! return [`None`]. |
24 | //! |
25 | //! The TLS destructor sets the state to `Destroyed` and drops the current value. |
26 | //! |
27 | //! To simplify the code, we make `LazyStorage` generic over the destroyed state |
28 | //! and use the `!` type (never type) as type parameter for `!Drop` types. This |
29 | //! eliminates the `Destroyed` state for these values, which can allow more niche |
30 | //! optimizations to occur for the `State` enum. For `Drop` types, `()` is used. |
31 | |
32 | use crate::cell::Cell; |
33 | use crate::ptr; |
34 | |
35 | mod eager; |
36 | mod lazy; |
37 | |
38 | pub use eager::Storage as EagerStorage; |
39 | pub use lazy::Storage as LazyStorage; |
40 | |
41 | #[doc (hidden)] |
42 | #[allow_internal_unstable ( |
43 | thread_local_internals, |
44 | cfg_target_thread_local, |
45 | thread_local, |
46 | never_type |
47 | )] |
48 | #[allow_internal_unsafe ] |
49 | #[unstable (feature = "thread_local_internals" , issue = "none" )] |
50 | #[rustc_macro_transparency = "semitransparent" ] |
51 | pub macro thread_local_inner { |
52 | // NOTE: we cannot import `LocalKey`, `LazyStorage` or `EagerStorage` with a `use` because that |
53 | // can shadow user provided type or type alias with a matching name. Please update the shadowing |
54 | // test in `tests/thread.rs` if these types are renamed. |
55 | |
56 | // Used to generate the `LocalKey` value for const-initialized thread locals. |
57 | (@key $t:ty, const $init:expr) => {{ |
58 | const __INIT: $t = $init; |
59 | |
60 | unsafe { |
61 | $crate::thread::LocalKey::new(const { |
62 | if $crate::mem::needs_drop::<$t>() { |
63 | |_| { |
64 | #[thread_local] |
65 | static VAL: $crate::thread::local_impl::EagerStorage<$t> |
66 | = $crate::thread::local_impl::EagerStorage::new(__INIT); |
67 | VAL.get() |
68 | } |
69 | } else { |
70 | |_| { |
71 | #[thread_local] |
72 | static VAL: $t = __INIT; |
73 | &VAL |
74 | } |
75 | } |
76 | }) |
77 | } |
78 | }}, |
79 | |
80 | // used to generate the `LocalKey` value for `thread_local!` |
81 | (@key $t:ty, $init:expr) => {{ |
82 | #[inline] |
83 | fn __init() -> $t { |
84 | $init |
85 | } |
86 | |
87 | unsafe { |
88 | $crate::thread::LocalKey::new(const { |
89 | if $crate::mem::needs_drop::<$t>() { |
90 | |init| { |
91 | #[thread_local] |
92 | static VAL: $crate::thread::local_impl::LazyStorage<$t, ()> |
93 | = $crate::thread::local_impl::LazyStorage::new(); |
94 | VAL.get_or_init(init, __init) |
95 | } |
96 | } else { |
97 | |init| { |
98 | #[thread_local] |
99 | static VAL: $crate::thread::local_impl::LazyStorage<$t, !> |
100 | = $crate::thread::local_impl::LazyStorage::new(); |
101 | VAL.get_or_init(init, __init) |
102 | } |
103 | } |
104 | }) |
105 | } |
106 | }}, |
107 | ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { |
108 | $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = |
109 | $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); |
110 | }, |
111 | } |
112 | |
113 | #[rustc_macro_transparency = "semitransparent" ] |
114 | pub(crate) macro local_pointer { |
115 | () => {}, |
116 | ($vis:vis static $name:ident; $($rest:tt)*) => { |
117 | #[thread_local] |
118 | $vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new(); |
119 | $crate::sys::thread_local::local_pointer! { $($rest)* } |
120 | }, |
121 | } |
122 | |
123 | pub(crate) struct LocalPointer { |
124 | p: Cell<*mut ()>, |
125 | } |
126 | |
127 | impl LocalPointer { |
128 | pub const fn __new() -> LocalPointer { |
129 | LocalPointer { p: Cell::new(ptr::null_mut()) } |
130 | } |
131 | |
132 | pub fn get(&self) -> *mut () { |
133 | self.p.get() |
134 | } |
135 | |
136 | pub fn set(&self, p: *mut ()) { |
137 | self.p.set(val:p) |
138 | } |
139 | } |
140 | |