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}