std/sys/thread_local/mod.rs
1//! Implementation of the `thread_local` macro.
2//!
3//! There are three different thread-local implementations:
4//! * Some targets lack threading support, and hence have only one thread, so
5//! the TLS data is stored in a normal `static`.
6//! * Some targets support TLS natively via the dynamic linker and C runtime.
7//! * On some targets, the OS provides a library-based TLS implementation. The
8//! TLS data is heap-allocated and referenced using a TLS key.
9//!
10//! Each implementation provides a macro which generates the `LocalKey` `const`
11//! used to reference the TLS variable, along with the necessary helper structs
12//! to track the initialization/destruction state of the variable.
13//!
14//! Additionally, this module contains abstractions for the OS interfaces used
15//! for these implementations.
16
17#![cfg_attr(test, allow(unused))]
18#![doc(hidden)]
19#![forbid(unsafe_op_in_unsafe_fn)]
20#![unstable(
21 feature = "thread_local_internals",
22 reason = "internal details of the thread_local macro",
23 issue = "none"
24)]
25
26cfg_if::cfg_if! {
27 if #[cfg(any(
28 all(target_family = "wasm", not(target_feature = "atomics")),
29 target_os = "uefi",
30 target_os = "zkvm",
31 target_os = "trusty",
32 ))] {
33 mod statik;
34 pub use statik::{EagerStorage, LazyStorage, thread_local_inner};
35 pub(crate) use statik::{LocalPointer, local_pointer};
36 } else if #[cfg(target_thread_local)] {
37 mod native;
38 pub use native::{EagerStorage, LazyStorage, thread_local_inner};
39 pub(crate) use native::{LocalPointer, local_pointer};
40 } else {
41 mod os;
42 pub use os::{Storage, thread_local_inner};
43 pub(crate) use os::{LocalPointer, local_pointer};
44 }
45}
46
47/// The native TLS implementation needs a way to register destructors for its data.
48/// This module contains platform-specific implementations of that register.
49///
50/// It turns out however that most platforms don't have a way to register a
51/// destructor for each variable. On these platforms, we keep track of the
52/// destructors ourselves and register (through the [`guard`] module) only a
53/// single callback that runs all of the destructors in the list.
54#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))]
55pub(crate) mod destructors {
56 cfg_if::cfg_if! {
57 if #[cfg(any(
58 target_os = "linux",
59 target_os = "android",
60 target_os = "fuchsia",
61 target_os = "redox",
62 target_os = "hurd",
63 target_os = "netbsd",
64 target_os = "dragonfly"
65 ))] {
66 mod linux_like;
67 mod list;
68 pub(super) use linux_like::register;
69 pub(super) use list::run;
70 } else {
71 mod list;
72 pub(super) use list::register;
73 pub(crate) use list::run;
74 }
75 }
76}
77
78/// This module provides a way to schedule the execution of the destructor list
79/// and the [runtime cleanup](crate::rt::thread_cleanup) function. Calling `enable`
80/// should ensure that these functions are called at the right times.
81pub(crate) mod guard {
82 cfg_if::cfg_if! {
83 if #[cfg(all(target_thread_local, target_vendor = "apple"))] {
84 mod apple;
85 pub(crate) use apple::enable;
86 } else if #[cfg(target_os = "windows")] {
87 mod windows;
88 pub(crate) use windows::enable;
89 } else if #[cfg(any(
90 all(target_family = "wasm", not(
91 all(target_os = "wasi", target_env = "p1", target_feature = "atomics")
92 )),
93 target_os = "uefi",
94 target_os = "zkvm",
95 target_os = "trusty",
96 ))] {
97 pub(crate) fn enable() {
98 // FIXME: Right now there is no concept of "thread exit" on
99 // wasm, but this is likely going to show up at some point in
100 // the form of an exported symbol that the wasm runtime is going
101 // to be expected to call. For now we just leak everything, but
102 // if such a function starts to exist it will probably need to
103 // iterate the destructor list with these functions:
104 #[cfg(all(target_family = "wasm", target_feature = "atomics"))]
105 #[allow(unused)]
106 use super::destructors::run;
107 #[allow(unused)]
108 use crate::rt::thread_cleanup;
109 }
110 } else if #[cfg(any(
111 target_os = "hermit",
112 target_os = "xous",
113 ))] {
114 // `std` is the only runtime, so it just calls the destructor functions
115 // itself when the time comes.
116 pub(crate) fn enable() {}
117 } else if #[cfg(target_os = "solid_asp3")] {
118 mod solid;
119 pub(crate) use solid::enable;
120 } else {
121 mod key;
122 pub(crate) use key::enable;
123 }
124 }
125}
126
127/// `const`-creatable TLS keys.
128///
129/// Most OSs without native TLS will provide a library-based way to create TLS
130/// storage. For each TLS variable, we create a key, which can then be used to
131/// reference an entry in a thread-local table. This then associates each key
132/// with a pointer which we can get and set to store our data.
133pub(crate) mod key {
134 cfg_if::cfg_if! {
135 if #[cfg(any(
136 all(
137 not(target_vendor = "apple"),
138 not(target_family = "wasm"),
139 target_family = "unix",
140 ),
141 target_os = "teeos",
142 all(target_os = "wasi", target_env = "p1", target_feature = "atomics"),
143 ))] {
144 mod racy;
145 mod unix;
146 #[cfg(test)]
147 mod tests;
148 pub(super) use racy::LazyKey;
149 pub(super) use unix::{Key, set};
150 #[cfg(any(not(target_thread_local), test))]
151 pub(super) use unix::get;
152 use unix::{create, destroy};
153 } else if #[cfg(all(not(target_thread_local), target_os = "windows"))] {
154 #[cfg(test)]
155 mod tests;
156 mod windows;
157 pub(super) use windows::{Key, LazyKey, get, run_dtors, set};
158 } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
159 mod racy;
160 mod sgx;
161 #[cfg(test)]
162 mod tests;
163 pub(super) use racy::LazyKey;
164 pub(super) use sgx::{Key, get, set};
165 use sgx::{create, destroy};
166 } else if #[cfg(target_os = "xous")] {
167 mod racy;
168 #[cfg(test)]
169 mod tests;
170 mod xous;
171 pub(super) use racy::LazyKey;
172 pub(crate) use xous::destroy_tls;
173 pub(super) use xous::{Key, get, set};
174 use xous::{create, destroy};
175 }
176 }
177}
178
179/// Run a callback in a scenario which must not unwind (such as a `extern "C"
180/// fn` declared in a user crate). If the callback unwinds anyway, then
181/// `rtabort` with a message about thread local panicking on drop.
182#[inline]
183#[allow(dead_code)]
184fn abort_on_dtor_unwind(f: impl FnOnce()) {
185 // Using a guard like this is lower cost.
186 let guard = DtorUnwindGuard;
187 f();
188 core::mem::forget(guard);
189
190 struct DtorUnwindGuard;
191 impl Drop for DtorUnwindGuard {
192 #[inline]
193 fn drop(&mut self) {
194 // This is not terribly descriptive, but it doesn't need to be as we'll
195 // already have printed a panic message at this point.
196 rtabort!("thread local panicked on drop");
197 }
198 }
199}