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