From 9212f4070ecaf4277de5f268f2ddfe56585be745 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Sun, 31 Oct 2021 16:08:12 +0000 Subject: [PATCH 1/2] 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). --- .../std/src/sys/windows/thread_local_dtor.rs | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) 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; -pub use crate::sys_common::thread_local_dtor::register_dtor_fallback as register_dtor; +// 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; + +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(); + } +} From 1048651fa3d0c8d5ac98a616810d1cf2b1fe80c0 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Mon, 1 Nov 2021 15:05:57 +0000 Subject: [PATCH 2/2] Run destructors from existing tls callback --- .../std/src/sys/windows/thread_local_dtor.rs | 35 +++++++------------ .../std/src/sys/windows/thread_local_key.rs | 2 ++ 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/library/std/src/sys/windows/thread_local_dtor.rs b/library/std/src/sys/windows/thread_local_dtor.rs index bf865e0fd72..25d1c6e8e87 100644 --- a/library/std/src/sys/windows/thread_local_dtor.rs +++ b/library/std/src/sys/windows/thread_local_dtor.rs @@ -3,7 +3,6 @@ #![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] @@ -13,27 +12,17 @@ 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; - -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(); +/// Runs destructors. This should not be called until thread exit. +pub 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(); } diff --git a/library/std/src/sys/windows/thread_local_key.rs b/library/std/src/sys/windows/thread_local_key.rs index 0bc51114665..ec670238e6f 100644 --- a/library/std/src/sys/windows/thread_local_key.rs +++ b/library/std/src/sys/windows/thread_local_key.rs @@ -196,6 +196,8 @@ pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c:: unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID) { if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH { run_dtors(); + #[cfg(target_thread_local)] + super::thread_local_dtor::run_keyless_dtors(); } // See comments above for what this is doing. Note that we don't need this