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