std/sys/thread_local/guard/key.rs
1//! A lot of UNIX platforms don't have a specialized way to register TLS
2//! destructors for native TLS. Instead, we use one TLS key with a destructor
3//! that will run all native TLS destructors in the destructor list.
4
5use crate::ptr;
6use crate::sys::thread_local::key::{LazyKey, set};
7
8#[cfg(target_thread_local)]
9pub fn enable() {
10 use crate::sys::thread_local::destructors;
11
12 static DTORS: LazyKey = LazyKey::new(Some(run));
13
14 // Setting the key value to something other than NULL will result in the
15 // destructor being run at thread exit.
16 unsafe {
17 set(DTORS.force(), ptr::without_provenance_mut(1));
18 }
19
20 unsafe extern "C" fn run(_: *mut u8) {
21 unsafe {
22 destructors::run();
23 // On platforms with `__cxa_thread_atexit_impl`, `destructors::run`
24 // does nothing on newer systems as the TLS destructors are
25 // registered with the system. But because all of those platforms
26 // call the destructors of TLS keys after the registered ones, this
27 // function will still be run last (at the time of writing).
28 crate::rt::thread_cleanup();
29 }
30 }
31}
32
33/// On platforms with key-based TLS, the system runs the destructors for us.
34/// We still have to make sure that [`crate::rt::thread_cleanup`] is called,
35/// however. This is done by defering the execution of a TLS destructor to
36/// the next round of destruction inside the TLS destructors.
37#[cfg(not(target_thread_local))]
38pub fn enable() {
39 const DEFER: *mut u8 = ptr::without_provenance_mut(1);
40 const RUN: *mut u8 = ptr::without_provenance_mut(2);
41
42 static CLEANUP: LazyKey = LazyKey::new(Some(run));
43
44 unsafe { set(CLEANUP.force(), DEFER) }
45
46 unsafe extern "C" fn run(state: *mut u8) {
47 if state == DEFER {
48 // Make sure that this function is run again in the next round of
49 // TLS destruction. If there is no futher round, there will be leaks,
50 // but that's okay, `thread_cleanup` is not guaranteed to be called.
51 unsafe { set(CLEANUP.force(), RUN) }
52 } else {
53 debug_assert_eq!(state, RUN);
54 // If the state is still RUN in the next round of TLS destruction,
55 // it means that no other TLS destructors defined by this runtime
56 // have been run, as they would have set the state to DEFER.
57 crate::rt::thread_cleanup();
58 }
59 }
60}