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.
1617#![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#![deny(
26 clippy::arithmetic_side_effects,
27 clippy::expect_used,
28 clippy::unwrap_used,
29 clippy::indexing_slicing,
30 clippy::panic,
31 clippy::unreachable,
32 clippy::unimplemented,
33 reason = "TLS accesses must not call the global allocator, including via panic (#160930)"
34)]
3536cfg_select! {
37 any(
38 all(target_family = "wasm", not(target_feature = "atomics"), not(target_env = "p3")),
39 target_os = "uefi",
40 target_os = "zkvm",
41 target_os = "trusty",
42 target_os = "vexos",
43 ) => {
44mod no_threads;
45pub use no_threads::{EagerStorage, LazyStorage, thread_local_inner};
46pub(crate) use no_threads::{LocalPointer, local_pointer};
47 }
48 target_thread_local => {
49mod native;
50pub use native::{EagerStorage, LazyStorage, thread_local_inner};
51pub(crate) use native::{LocalPointer, local_pointer};
52 }
53_ => {
54mod os;
55pub(crate) use os::{LocalPointer, local_pointer};
56pub use os::{Storage, thread_local_inner, value_align};
57 }
58}
5960/// The native TLS implementation needs a way to register destructors for its data.
61/// This module contains platform-specific implementations of that register.
62///
63/// It turns out however that most platforms don't have a way to register a
64/// destructor for each variable. On these platforms, we keep track of the
65/// destructors ourselves and register (through the [`guard`] module) only a
66/// single callback that runs all of the destructors in the list.
67#[cfg(all(
68 target_thread_local,
69 not(all(target_family = "wasm", not(target_feature = "atomics"), not(target_env = "p3")))
70))]
71pub(crate) mod destructors {
72cfg_select! {
73 any(
74 target_os = "linux",
75 target_os = "android",
76 target_os = "fuchsia",
77 target_os = "redox",
78 target_os = "hurd",
79 target_os = "netbsd",
80 target_os = "dragonfly"
81) => {
82mod linux_like;
83mod list;
84pub(super) use linux_like::register;
85pub(super) use list::run;
86 }
87_ => {
88mod list;
89pub(super) use list::register;
90pub(crate) use list::run;
91 }
92 }
93}
9495/// This module provides a way to schedule the execution of the destructor list
96/// and the [runtime cleanup](crate::rt::thread_cleanup) function. Calling `enable`
97/// sets up the current thread to ensure that these functions are called at the right times.
98pub(crate) mod guard {
99cfg_select! {
100 all(target_thread_local, target_vendor = "apple") => {
101mod apple;
102pub(crate) use apple::enable;
103 }
104 target_os = "windows" => {
105mod windows;
106pub(crate) use windows::enable;
107 }
108 any(
109 all(target_family = "wasm", not(target_env = "p3")),
110 target_os = "uefi",
111 target_os = "zkvm",
112 target_os = "trusty",
113 target_os = "vexos",
114 ) => {
115pub(crate) fn enable() {
116// FIXME: Right now there is no concept of "thread exit" on
117 // wasm, but this is likely going to show up at some point in
118 // the form of an exported symbol that the wasm runtime is going
119 // to be expected to call. For now we just leak everything, but
120 // if such a function starts to exist it will probably need to
121 // iterate the destructor list with these functions:
122#[cfg(all(target_family = "wasm", target_feature = "atomics"))]
123 #[allow(unused)]
124use super::destructors::run;
125#[allow(unused)]
126use crate::rt::thread_cleanup;
127 }
128 }
129 any(target_os = "hermit", target_os = "xous") => {
130// `std` is the only runtime, so it just calls the destructor functions
131 // itself when the time comes.
132pub(crate) fn enable() {}
133 }
134 target_os = "solid_asp3" => {
135mod solid;
136pub(crate) use solid::enable;
137 }
138_ => {
139mod key;
140pub(crate) use key::enable;
141 }
142 }
143}
144145/// `const`-creatable TLS keys.
146///
147/// Most OSs without native TLS will provide a library-based way to create TLS
148/// storage. For each TLS variable, we create a key, which can then be used to
149/// reference an entry in a thread-local table. This then associates each key
150/// with a pointer which we can get and set to store our data.
151pub(crate) mod key {
152cfg_select! {
153 any(
154 all(not(target_vendor = "apple"), not(target_family = "wasm"), target_family = "unix"),
155 all(not(target_thread_local), target_vendor = "apple"),
156 target_os = "teeos",
157 all(target_os = "wasi", target_env = "p3"),
158 ) => {
159mod racy;
160mod unix;
161#[cfg(test)]
162mod tests;
163pub(super) use racy::LazyKey;
164#[cfg(any(not(target_thread_local), test))]
165pub(super) use unix::get;
166pub(super) use unix::{Key, set};
167use unix::{create, destroy};
168 }
169 all(not(target_thread_local), target_os = "windows") => {
170#[cfg(test)]
171mod tests;
172mod windows;
173pub(super) use windows::{Key, LazyKey, get, run_dtors, set};
174 }
175 all(target_vendor = "fortanix", target_env = "sgx") => {
176mod racy;
177mod sgx;
178#[cfg(test)]
179mod tests;
180pub(super) use racy::LazyKey;
181pub(super) use sgx::{Key, get, set};
182use sgx::{create, destroy};
183 }
184 target_os = "xous" => {
185mod racy;
186#[cfg(test)]
187mod tests;
188mod xous;
189pub(super) use racy::LazyKey;
190pub(crate) use xous::destroy_tls;
191pub(super) use xous::{Key, get, set};
192use xous::{create, destroy};
193 }
194 target_os = "motor" => {
195mod racy;
196#[cfg(test)]
197mod tests;
198pub(super) use moto_rt::tls::{Key, get, set};
199use moto_rt::tls::{create, destroy};
200pub(super) use racy::LazyKey;
201 }
202_ => {}
203 }
204}
205206/// Run a callback in a scenario which must not unwind (such as a `extern "C"
207/// fn` declared in a user crate). If the callback unwinds anyway, then
208/// `rtabort` with a message about thread local panicking on drop.
209#[inline]
210#[allow(dead_code)]
211fn abort_on_dtor_unwind(f: impl FnOnce()) {
212// Using a guard like this is lower cost.
213let guard = DtorUnwindGuard;
214f();
215 core::mem::forget(guard);
216217struct DtorUnwindGuard;
218impl Dropfor DtorUnwindGuard {
219#[inline]
220fn drop(&mut self) {
221// This is not terribly descriptive, but it doesn't need to be as we'll
222 // already have printed a panic message at this point.
223{
if let Some(mut out) = crate::sys::stdio::panic_output() {
let _ =
crate::io::Write::write_fmt(&mut out,
format_args!("fatal runtime error: {0}, aborting\n",
format_args!("thread local panicked on drop")));
};
crate::process::abort();
};rtabort!("thread local panicked on drop");
224 }
225 }
226}