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