11use crate :: cell:: UnsafeCell ;
22use crate :: ptr;
33use crate :: sync:: atomic:: {
4- AtomicPtr , AtomicU32 ,
4+ AtomicBool , AtomicPtr , AtomicU32 ,
55 Ordering :: { AcqRel , Acquire , Relaxed , Release } ,
66} ;
77use crate :: sys:: c;
88
99#[ cfg( test) ]
1010mod tests;
1111
12+ /// An optimization hint. The compiler is often smart enough to know if an atomic
13+ /// is never set and can remove dead code based on that fact.
14+ static HAS_DTORS : AtomicBool = AtomicBool :: new ( false ) ;
15+
16+ // Using a per-thread list avoids the problems in synchronizing global state.
17+ #[ thread_local]
18+ #[ cfg( target_thread_local) ]
19+ static mut DESTRUCTORS : Vec < ( * mut u8 , unsafe extern "C" fn ( * mut u8 ) ) > = Vec :: new ( ) ;
20+
21+ // Ensure this can never be inlined because otherwise this may break in dylibs.
22+ // See #44391.
23+ #[ inline( never) ]
24+ #[ cfg( target_thread_local) ]
25+ pub unsafe fn register_keyless_dtor ( t : * mut u8 , dtor : unsafe extern "C" fn ( * mut u8 ) ) {
26+ DESTRUCTORS . push ( ( t, dtor) ) ;
27+ HAS_DTORS . store ( true , Relaxed ) ;
28+ }
29+
30+ #[ inline( never) ] // See comment above
31+ #[ cfg( target_thread_local) ]
32+ /// Runs destructors. This should not be called until thread exit.
33+ unsafe fn run_keyless_dtors ( ) {
34+ // Drop all the destructors.
35+ //
36+ // Note: While this is potentially an infinite loop, it *should* be
37+ // the case that this loop always terminates because we provide the
38+ // guarantee that a TLS key cannot be set after it is flagged for
39+ // destruction.
40+ while let Some ( ( ptr, dtor) ) = DESTRUCTORS . pop ( ) {
41+ ( dtor) ( ptr) ;
42+ }
43+ // We're done so free the memory.
44+ DESTRUCTORS = Vec :: new ( ) ;
45+ }
46+
1247type Key = c:: DWORD ;
1348type Dtor = unsafe extern "C" fn ( * mut u8 ) ;
1449
@@ -156,6 +191,8 @@ static DTORS: AtomicPtr<StaticKey> = AtomicPtr::new(ptr::null_mut());
156191/// Should only be called once per key, otherwise loops or breaks may occur in
157192/// the linked list.
158193unsafe fn register_dtor ( key : & ' static StaticKey ) {
194+ // Ensure this is never run when native thread locals are available.
195+ assert_eq ! ( false , cfg!( target_thread_local) ) ;
159196 let this = <* const StaticKey >:: cast_mut ( key) ;
160197 // Use acquire ordering to pass along the changes done by the previously
161198 // registered keys when we store the new head with release ordering.
@@ -167,6 +204,7 @@ unsafe fn register_dtor(key: &'static StaticKey) {
167204 Err ( new) => head = new,
168205 }
169206 }
207+ HAS_DTORS . store ( true , Release ) ;
170208}
171209
172210// -------------------------------------------------------------------------
@@ -240,10 +278,14 @@ pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c::
240278
241279#[ allow( dead_code, unused_variables) ]
242280unsafe extern "system" fn on_tls_callback ( h : c:: LPVOID , dwReason : c:: DWORD , pv : c:: LPVOID ) {
281+ if !HAS_DTORS . load ( Acquire ) {
282+ return ;
283+ }
243284 if dwReason == c:: DLL_THREAD_DETACH || dwReason == c:: DLL_PROCESS_DETACH {
285+ #[ cfg( not( target_thread_local) ) ]
244286 run_dtors ( ) ;
245287 #[ cfg( target_thread_local) ]
246- super :: thread_local_dtor :: run_keyless_dtors ( ) ;
288+ run_keyless_dtors ( ) ;
247289 }
248290
249291 // See comments above for what this is doing. Note that we don't need this
0 commit comments