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