about summary refs log tree commit diff
path: root/library/std/src/sys/windows/thread_local_key.rs
diff options
context:
space:
mode:
authorChris Denton <chris@chrisdenton.dev>2023-07-20 23:05:32 +0100
committerChris Denton <chris@chrisdenton.dev>2023-07-20 23:27:24 +0100
commit40e116489feabac4289f4a5d65913a28dc36927d (patch)
tree49d9c943a62fb340fc06754fcdf4ccd7cc29be07 /library/std/src/sys/windows/thread_local_key.rs
parent1554942cdc0099e081d5cd673d45f8a09cfd0eb0 (diff)
downloadrust-40e116489feabac4289f4a5d65913a28dc36927d.tar.gz
rust-40e116489feabac4289f4a5d65913a28dc36927d.zip
Minor improvements to Windows TLS dtors
Diffstat (limited to 'library/std/src/sys/windows/thread_local_key.rs')
-rw-r--r--library/std/src/sys/windows/thread_local_key.rs46
1 files changed, 44 insertions, 2 deletions
diff --git a/library/std/src/sys/windows/thread_local_key.rs b/library/std/src/sys/windows/thread_local_key.rs
index 17628b7579b..036d96596e9 100644
--- a/library/std/src/sys/windows/thread_local_key.rs
+++ b/library/std/src/sys/windows/thread_local_key.rs
@@ -1,7 +1,7 @@
 use crate::cell::UnsafeCell;
 use crate::ptr;
 use crate::sync::atomic::{
-    AtomicPtr, AtomicU32,
+    AtomicBool, AtomicPtr, AtomicU32,
     Ordering::{AcqRel, Acquire, Relaxed, Release},
 };
 use crate::sys::c;
@@ -9,6 +9,41 @@ use crate::sys::c;
 #[cfg(test)]
 mod tests;
 
+/// An optimization hint. The compiler is often smart enough to know if an atomic
+/// is never set and can remove dead code based on that fact.
+static HAS_DTORS: AtomicBool = AtomicBool::new(false);
+
+// Using a per-thread list avoids the problems in synchronizing global state.
+#[thread_local]
+#[cfg(target_thread_local)]
+static mut DESTRUCTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
+
+// Ensure this can never be inlined because otherwise this may break in dylibs.
+// See #44391.
+#[inline(never)]
+#[cfg(target_thread_local)]
+pub unsafe fn register_keyless_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+    DESTRUCTORS.push((t, dtor));
+    HAS_DTORS.store(true, Relaxed);
+}
+
+#[inline(never)] // See comment above
+#[cfg(target_thread_local)]
+/// Runs destructors. This should not be called until thread exit.
+unsafe fn run_keyless_dtors() {
+    // Drop all the destructors.
+    //
+    // Note: While this is potentially an infinite loop, it *should* be
+    // the case that this loop always terminates because we provide the
+    // guarantee that a TLS key cannot be set after it is flagged for
+    // destruction.
+    while let Some((ptr, dtor)) = DESTRUCTORS.pop() {
+        (dtor)(ptr);
+    }
+    // We're done so free the memory.
+    DESTRUCTORS = Vec::new();
+}
+
 type Key = c::DWORD;
 type Dtor = unsafe extern "C" fn(*mut u8);
 
@@ -156,6 +191,8 @@ static DTORS: AtomicPtr<StaticKey> = AtomicPtr::new(ptr::null_mut());
 /// Should only be called once per key, otherwise loops or breaks may occur in
 /// the linked list.
 unsafe fn register_dtor(key: &'static StaticKey) {
+    // Ensure this is never run when native thread locals are available.
+    assert_eq!(false, cfg!(target_thread_local));
     let this = <*const StaticKey>::cast_mut(key);
     // Use acquire ordering to pass along the changes done by the previously
     // registered keys when we store the new head with release ordering.
@@ -167,6 +204,7 @@ unsafe fn register_dtor(key: &'static StaticKey) {
             Err(new) => head = new,
         }
     }
+    HAS_DTORS.store(true, Release);
 }
 
 // -------------------------------------------------------------------------
@@ -240,10 +278,14 @@ pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c::
 
 #[allow(dead_code, unused_variables)]
 unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID) {
+    if !HAS_DTORS.load(Acquire) {
+        return;
+    }
     if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH {
+        #[cfg(not(target_thread_local))]
         run_dtors();
         #[cfg(target_thread_local)]
-        super::thread_local_dtor::run_keyless_dtors();
+        run_keyless_dtors();
     }
 
     // See comments above for what this is doing. Note that we don't need this