about summary refs log tree commit diff
diff options
context:
space:
mode:
authorChris Denton <christophersdenton@gmail.com>2021-10-31 16:08:12 +0000
committerChris Denton <christophersdenton@gmail.com>2021-10-31 16:09:35 +0000
commit9212f4070ecaf4277de5f268f2ddfe56585be745 (patch)
treeccfb940522559e68ae7f47cff6847b8b6e9e4f2b
parent68b554e6af18726fe6fa8de2134c59c441e0b019 (diff)
downloadrust-9212f4070ecaf4277de5f268f2ddfe56585be745.tar.gz
rust-9212f4070ecaf4277de5f268f2ddfe56585be745.zip
Windows thread-local keyless drop
`#[thread_local]` allows us to maintain a per-thread list of destructors. This also avoids the need to synchronize global data (which is particularly tricky within the TLS callback function).
-rw-r--r--library/std/src/sys/windows/thread_local_dtor.rs37
1 files changed, 36 insertions, 1 deletions
diff --git a/library/std/src/sys/windows/thread_local_dtor.rs b/library/std/src/sys/windows/thread_local_dtor.rs
index 7be13bc4b2b..bf865e0fd72 100644
--- a/library/std/src/sys/windows/thread_local_dtor.rs
+++ b/library/std/src/sys/windows/thread_local_dtor.rs
@@ -1,4 +1,39 @@
+//! Implements thread-local destructors that are not associated with any
+//! particular data.
+
 #![unstable(feature = "thread_local_internals", issue = "none")]
 #![cfg(target_thread_local)]
+use super::c;
+
+// Using a per-thread list avoids the problems in synchronizing global state.
+#[thread_local]
+static mut DESTRUCTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
+
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+    DESTRUCTORS.push((t, dtor));
+}
+
+// See windows/thread_local_keys.rs for an explanation of this callback function.
+// The short version is that all the function pointers in the `.CRT$XL*` array
+// will be called whenever a thread or process starts or ends.
+
+#[link_section = ".CRT$XLD"]
+#[doc(hidden)]
+#[used]
+pub static TLS_CALLBACK: unsafe extern "system" fn(c::LPVOID, c::DWORD, c::LPVOID) = tls_callback;
 
-pub use crate::sys_common::thread_local_dtor::register_dtor_fallback as register_dtor;
+unsafe extern "system" fn tls_callback(_: c::LPVOID, reason: c::DWORD, _: c::LPVOID) {
+    if reason == c::DLL_THREAD_DETACH || reason == c::DLL_PROCESS_DETACH {
+        // 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.shrink_to_fit();
+    }
+}