Skip to main content

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#![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)]
35
36cfg_select! {
37    any(
38        all(target_family = "wasm", not(target_feature = "atomics"), not(target_os = "wasi")),
39        target_os = "uefi",
40        target_os = "zkvm",
41        target_os = "trusty",
42        target_os = "vexos",
43    ) => {
44        mod no_threads;
45        pub use no_threads::{EagerStorage, LazyStorage, thread_local_inner};
46        pub(crate) use no_threads::{LocalPointer, local_pointer};
47    }
48    target_thread_local => {
49        mod native;
50        pub use native::{EagerStorage, LazyStorage, thread_local_inner};
51        pub(crate) use native::{LocalPointer, local_pointer};
52    }
53    _ => {
54        mod os;
55        pub(crate) use os::{LocalPointer, local_pointer};
56        pub use os::{Storage, thread_local_inner, value_align};
57    }
58}
59
60/// 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_os = "wasi")))
70))]
71pub(crate) mod destructors {
72    cfg_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        ) => {
82            mod linux_like;
83            mod list;
84            pub(super) use linux_like::register;
85            pub(super) use list::run;
86        }
87        _ => {
88            mod list;
89            pub(super) use list::register;
90            pub(crate) use list::run;
91        }
92    }
93}
94
95/// 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 {
99    cfg_select! {
100        all(target_thread_local, target_vendor = "apple") => {
101            mod apple;
102            pub(crate) use apple::enable;
103        }
104        all(target_thread_local, target_os = "aix") => {
105            mod aix;
106            pub(crate) use aix::enable;
107        }
108        target_os = "windows" => {
109            mod windows;
110            pub(crate) use windows::enable;
111            #[cfg(not(target_thread_local))]
112            mod retained;
113            #[cfg(not(target_thread_local))]
114            pub(crate) use retained::{LOCAL_POINTER_NEEDS_DTOR, cleanup_pending};
115        }
116        any(
117            all(target_family = "wasm", not(target_os = "wasi")),
118            target_os = "uefi",
119            target_os = "zkvm",
120            target_os = "trusty",
121            target_os = "vexos",
122        ) => {
123            pub(crate) fn enable() {
124                // FIXME: Right now there is no concept of "thread exit" on
125                // wasm, but this is likely going to show up at some point in
126                // the form of an exported symbol that the wasm runtime is going
127                // to be expected to call. For now we just leak everything, but
128                // if such a function starts to exist it will probably need to
129                // iterate the destructor list with these functions:
130                #[cfg(all(target_family = "wasm", target_feature = "atomics"))]
131                #[allow(unused)]
132                use super::destructors::run;
133                #[allow(unused)]
134                use crate::rt::thread_cleanup;
135            }
136        }
137        any(target_os = "hermit", target_os = "xous") => {
138            // `std` is the only runtime, so it just calls the destructor functions
139            // itself when the time comes.
140            pub(crate) fn enable() {}
141            #[cfg(not(target_thread_local))]
142            mod retained;
143            #[cfg(not(target_thread_local))]
144            pub(crate) use retained::{LOCAL_POINTER_NEEDS_DTOR, cleanup_pending};
145        }
146        target_os = "solid_asp3" => {
147            mod solid;
148            pub(crate) use solid::enable;
149        }
150        _ => {
151            mod key;
152            pub(crate) use key::enable;
153            #[cfg(not(target_thread_local))]
154            pub(crate) use key::{LOCAL_POINTER_NEEDS_DTOR, cleanup_pending};
155        }
156    }
157}
158
159/// `const`-creatable TLS keys.
160///
161/// Most OSs without native TLS will provide a library-based way to create TLS
162/// storage. For each TLS variable, we create a key, which can then be used to
163/// reference an entry in a thread-local table. This then associates each key
164/// with a pointer which we can get and set to store our data.
165pub(crate) mod key {
166    cfg_select! {
167        any(
168            all(not(target_vendor = "apple"), not(target_family = "wasm"), target_family = "unix"),
169            all(not(target_thread_local), target_vendor = "apple"),
170            target_os = "teeos",
171            target_os = "wasi",
172        ) => {
173            mod racy;
174            mod unix;
175            #[cfg(test)]
176            mod tests;
177            pub(super) use racy::LazyKey;
178            #[cfg(any(not(target_thread_local), test))]
179            pub(super) use unix::get;
180            pub(super) use unix::{Key, set};
181            use unix::{create, destroy};
182        }
183        all(not(target_thread_local), target_os = "windows") => {
184            #[cfg(test)]
185            mod tests;
186            mod windows;
187            pub(super) use windows::{Key, LazyKey, get, run_dtors, set};
188        }
189        all(target_vendor = "fortanix", target_env = "sgx") => {
190            mod racy;
191            mod sgx;
192            #[cfg(test)]
193            mod tests;
194            pub(super) use racy::LazyKey;
195            pub(super) use sgx::{Key, get, set};
196            use sgx::{create, destroy};
197        }
198        target_os = "xous" => {
199            mod racy;
200            #[cfg(test)]
201            mod tests;
202            mod xous;
203            pub(super) use racy::LazyKey;
204            pub(crate) use xous::destroy_tls;
205            pub(super) use xous::{Key, get, set};
206            use xous::{create, destroy};
207        }
208        target_os = "motor" => {
209            mod racy;
210            #[cfg(test)]
211            mod tests;
212            pub(super) use moto_rt::tls::{Key, get, set};
213            use moto_rt::tls::{create, destroy};
214            pub(super) use racy::LazyKey;
215        }
216        _ => {}
217    }
218}
219
220/// Run a callback in a scenario which must not unwind (such as a `extern "C"
221/// fn` declared in a user crate). If the callback unwinds anyway, then
222/// `rtabort` with a message about thread local panicking on drop.
223#[inline]
224#[allow(dead_code)]
225fn abort_on_dtor_unwind(f: impl FnOnce()) {
226    // Using a guard like this is lower cost.
227    let guard = DtorUnwindGuard;
228    f();
229    core::mem::forget(guard);
230
231    struct DtorUnwindGuard;
232    impl Drop for DtorUnwindGuard {
233        #[inline]
234        fn drop(&mut self) {
235            // This is not terribly descriptive, but it doesn't need to be as we'll
236            // already have printed a panic message at this point.
237            rtabort!("thread local panicked on drop");
238        }
239    }
240}