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