| //! 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", |
| ) => { |
| 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}; |
| 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", |
| ) => { |
| 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}; |
| } |
| _ => {} |
| } |
| } |
| |
| /// 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"); |
| } |
| } |
| } |