| //! 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_select! { | |
| any( | |
| all(target_family = "wasm", not(target_feature = "atomics")), | |
| target_os = "uefi", | |
| target_os = "zkvm", | |
| target_os = "trusty", | |
| target_os = "vexos", | |
| ) => { | |
| mod no_threads; | |
| pub use no_threads::{EagerStorage, LazyStorage, thread_local_inner}; | |
| pub(crate) use no_threads::{LocalPointer, local_pointer}; | |
| } | |
| target_thread_local => { | |
| mod native; | |
| pub use native::{EagerStorage, LazyStorage, thread_local_inner}; | |
| pub(crate) use native::{LocalPointer, local_pointer}; | |
| } | |
| _ => { | |
| mod os; | |
| pub use os::{Storage, thread_local_inner, value_align}; | |
| 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_select! { | |
| 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; | |
| } | |
| _ => { | |
| 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_select! { | |
| all(target_thread_local, target_vendor = "apple") => { | |
| mod apple; | |
| pub(crate) use apple::enable; | |
| } | |
| target_os = "windows" => { | |
| mod windows; | |
| pub(crate) use windows::enable; | |
| } | |
| any( | |
| all(target_family = "wasm", not( | |
| all(target_os = "wasi", target_env = "p1", target_feature = "atomics") | |
| )), | |
| target_os = "uefi", | |
| target_os = "zkvm", | |
| target_os = "trusty", | |
| target_os = "vexos", | |
| ) => { | |
| 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; | |
| } | |
| } | |
| 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() {} | |
| } | |
| target_os = "solid_asp3" => { | |
| mod solid; | |
| pub(crate) use solid::enable; | |
| } | |
| _ => { | |
| 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_select! { | |
| any( | |
| all( | |
| not(target_vendor = "apple"), | |
| not(target_family = "wasm"), | |
| target_family = "unix", | |
| ), | |
| all(not(target_thread_local), target_vendor = "apple"), | |
| target_os = "teeos", | |
| all(target_os = "wasi", target_env = "p1", target_feature = "atomics"), | |
| ) => { | |
| 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}; | |
| } | |
| all(not(target_thread_local), target_os = "windows") => { | |
| #[cfg(test)] | |
| mod tests; | |
| mod windows; | |
| pub(super) use windows::{Key, LazyKey, get, run_dtors, set}; | |
| } | |
| 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}; | |
| } | |
| 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}; | |
| } | |
| target_os = "motor" => { | |
| mod racy; | |
| #[cfg(test)] | |
| mod tests; | |
| pub(super) use racy::LazyKey; | |
| pub(super) use moto_rt::tls::{Key, get, set}; | |
| use moto_rt::tls::{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"); | |
| } | |
| } | |
| } |