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}