1
1
use crate :: cell:: UnsafeCell ;
2
2
use crate :: ptr;
3
3
use crate :: sync:: atomic:: {
4
- AtomicPtr , AtomicU32 ,
4
+ AtomicBool , AtomicPtr , AtomicU32 ,
5
5
Ordering :: { AcqRel , Acquire , Relaxed , Release } ,
6
6
} ;
7
7
use crate :: sys:: c;
8
8
9
9
#[ cfg( test) ]
10
10
mod tests;
11
11
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
+
12
47
type Key = c:: DWORD ;
13
48
type Dtor = unsafe extern "C" fn ( * mut u8 ) ;
14
49
@@ -156,6 +191,8 @@ static DTORS: AtomicPtr<StaticKey> = AtomicPtr::new(ptr::null_mut());
156
191
/// Should only be called once per key, otherwise loops or breaks may occur in
157
192
/// the linked list.
158
193
unsafe 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) ) ;
159
196
let this = <* const StaticKey >:: cast_mut ( key) ;
160
197
// Use acquire ordering to pass along the changes done by the previously
161
198
// registered keys when we store the new head with release ordering.
@@ -167,6 +204,7 @@ unsafe fn register_dtor(key: &'static StaticKey) {
167
204
Err ( new) => head = new,
168
205
}
169
206
}
207
+ HAS_DTORS . store ( true , Release ) ;
170
208
}
171
209
172
210
// -------------------------------------------------------------------------
@@ -240,10 +278,14 @@ pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c::
240
278
241
279
#[ allow( dead_code, unused_variables) ]
242
280
unsafe extern "system" fn on_tls_callback ( h : c:: LPVOID , dwReason : c:: DWORD , pv : c:: LPVOID ) {
281
+ if !HAS_DTORS . load ( Acquire ) {
282
+ return ;
283
+ }
243
284
if dwReason == c:: DLL_THREAD_DETACH || dwReason == c:: DLL_PROCESS_DETACH {
285
+ #[ cfg( not( target_thread_local) ) ]
244
286
run_dtors ( ) ;
245
287
#[ cfg( target_thread_local) ]
246
- super :: thread_local_dtor :: run_keyless_dtors ( ) ;
288
+ run_keyless_dtors ( ) ;
247
289
}
248
290
249
291
// See comments above for what this is doing. Note that we don't need this
0 commit comments