How do the thread local variables in the Rust standard library work? I looked at the code, but got lost in indirection. It seems that there are different configurations for thread local storage, an OS dependent mode and a fast mode. Which one is the default, and how do I choose which one to use? In particular, what are the implications of using thread local storage in a crate for the user of the crate?
Using thread local storage is simple enough, and the generated assembly looks really efficient, but I can not use the feature in a library without fully understanding the implications.
I've also asked:
With thread local storage (TLS), you can provide unique data for each thread that the process can access using a global index. One thread allocates the index, which can be used by the other threads to retrieve the unique data associated with the index.
This library provides the ThreadLocal type which allow a separate copy of an object to be used for each thread. This allows for per-object thread-local storage, unlike the standard library's thread_local! macro which only allows static thread-local storage. Documentation.
Thread Local Storage (TLS) is the method by which each thread in a given multithreaded process can allocate locations in which to store thread-specific data. Dynamically bound (run-time) thread-specific data is supported by way of the TLS API (TlsAlloc).
The different configurations you see are related to #[thread_local]
, a feature that is intended to replace the thread_local!
macro to make the user code more straightforward, e.g.:
#![feature(thread_local)]
#[thread_local]
pub static mut VAR: u64 = 42;
However, at the moment of writing, this feature is not fully implemented yet (you can find the tracking issue here). It is used internally in the compiler code though, and that is the magic you see in the actual 'fast' implementation in std::thread::LocalKey
:
#[thread_local]
#[cfg(all(
target_thread_local,
not(all(target_arch = "wasm32", not(target_feature = "atomics"))),
))]
static __KEY: $crate::thread::__FastLocalKeyInner<$t> =
$crate::thread::__FastLocalKeyInner::new();
Notice the #[thread_local]
attribute at the top. It is then translated down to LLVM IR, so the actual implementation of TLS (thread-local storage) is carried by LLVM and implements the ELF TLS models. This is the default configuration.
how do I choose which one to use?
You'll need to compile your own rustc
version with the target_thread_local
feature omitted. In that case, the os
variant of std::thread::LocalKey
will be used, and then, depending on a platform, it can use pthreads (Unix), or Windows API, or something else.
WebAssembly is a special case: as it doesn't support threads, TLS will be translated down to simple static variables.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With