1+ //! Ensures that thread-local destructors are run on thread exit.
2+
13#![ cfg( target_thread_local) ]
24#![ unstable( feature = "thread_local_internals" , issue = "none" ) ]
35
4- //! Provides thread-local destructors without an associated "key", which
5- //! can be more efficient.
6+ use crate :: ptr ;
7+ use crate :: sys :: common :: thread_local :: run_dtors ;
68
79// Since what appears to be glibc 2.18 this symbol has been shipped which
810// GCC and clang both use to invoke destructors in thread_local globals, so
1618// FIXME: The Rust compiler currently omits weakly function definitions (i.e.,
1719// __cxa_thread_atexit_impl) and its metadata from LLVM IR.
1820#[ no_sanitize( cfi, kcfi) ]
19- pub unsafe fn register_dtor ( t : * mut u8 , dtor : unsafe extern "C" fn ( * mut u8 ) ) {
21+ pub fn activate ( ) {
22+ use crate :: cell:: Cell ;
2023 use crate :: mem;
21- use crate :: sys_common:: thread_local_dtor :: register_dtor_fallback ;
24+ use crate :: sys_common:: thread_local_key :: StaticKey ;
2225
2326 /// This is necessary because the __cxa_thread_atexit_impl implementation
2427 /// std links to by default may be a C or C++ implementation that was not
@@ -41,64 +44,47 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
4144 > ;
4245 }
4346
44- if let Some ( f) = __cxa_thread_atexit_impl {
45- unsafe {
46- f (
47- mem:: transmute :: <
48- unsafe extern "C" fn ( * mut u8 ) ,
49- unsafe extern "C" fn ( * mut libc:: c_void ) ,
50- > ( dtor) ,
51- t. cast ( ) ,
52- & __dso_handle as * const _ as * mut _ ,
53- ) ;
47+ unsafe {
48+ if let Some ( atexit) = __cxa_thread_atexit_impl {
49+ #[ thread_local]
50+ static REGISTERED : Cell < bool > = Cell :: new ( false ) ;
51+ if !REGISTERED . get ( ) {
52+ atexit (
53+ mem:: transmute :: <
54+ unsafe extern "C" fn ( * mut u8 ) ,
55+ unsafe extern "C" fn ( * mut libc:: c_void ) ,
56+ > ( run_dtors) ,
57+ ptr:: null_mut ( ) ,
58+ & __dso_handle as * const _ as * mut _ ,
59+ ) ;
60+ REGISTERED . set ( true ) ;
61+ }
62+ } else {
63+ static KEY : StaticKey = StaticKey :: new ( Some ( run_dtors) ) ;
64+
65+ KEY . set ( ptr:: invalid_mut ( 1 ) ) ;
5466 }
55- return ;
5667 }
57- register_dtor_fallback ( t, dtor) ;
5868}
5969
60- // This implementation is very similar to register_dtor_fallback in
61- // sys_common/thread_local.rs. The main difference is that we want to hook into
62- // macOS's analog of the above linux function, _tlv_atexit. OSX will run the
63- // registered dtors before any TLS slots get freed, and when the main thread
70+ // We hook into macOS's analog of the above linux function, _tlv_atexit. OSX
71+ // will run `run_dtors` before any TLS slots get freed, and when the main thread
6472// exits.
65- //
66- // Unfortunately, calling _tlv_atexit while tls dtors are running is UB. The
67- // workaround below is to register, via _tlv_atexit, a custom DTOR list once per
68- // thread. thread_local dtors are pushed to the DTOR list without calling
69- // _tlv_atexit.
7073#[ cfg( any( target_os = "macos" , target_os = "ios" , target_os = "watchos" , target_os = "tvos" ) ) ]
71- pub unsafe fn register_dtor ( t : * mut u8 , dtor : unsafe extern "C" fn ( * mut u8 ) ) {
72- use crate :: cell:: { Cell , RefCell } ;
73- use crate :: ptr;
74-
75- #[ thread_local]
76- static REGISTERED : Cell < bool > = Cell :: new ( false ) ;
77-
78- #[ thread_local]
79- static DTORS : RefCell < Vec < ( * mut u8 , unsafe extern "C" fn ( * mut u8 ) ) > > = RefCell :: new ( Vec :: new ( ) ) ;
80-
81- if !REGISTERED . get ( ) {
82- _tlv_atexit ( run_dtors, ptr:: null_mut ( ) ) ;
83- REGISTERED . set ( true ) ;
84- }
74+ pub fn activate ( ) {
75+ use crate :: cell:: Cell ;
8576
8677 extern "C" {
8778 fn _tlv_atexit ( dtor : unsafe extern "C" fn ( * mut u8 ) , arg : * mut u8 ) ;
8879 }
8980
90- match DTORS . try_borrow_mut ( ) {
91- Ok ( mut dtors) => dtors. push ( ( t, dtor) ) ,
92- Err ( _) => rtabort ! ( "global allocator may not use TLS" ) ,
93- }
81+ #[ thread_local]
82+ static REGISTERED : Cell < bool > = Cell :: new ( false ) ;
9483
95- unsafe extern "C" fn run_dtors ( _: * mut u8 ) {
96- let mut list = DTORS . take ( ) ;
97- while !list. is_empty ( ) {
98- for ( ptr, dtor) in list {
99- dtor ( ptr) ;
100- }
101- list = DTORS . take ( ) ;
84+ if !REGISTERED . get ( ) {
85+ unsafe {
86+ _tlv_atexit ( run_dtors, ptr:: null_mut ( ) ) ;
87+ REGISTERED . set ( true ) ;
10288 }
10389 }
10490}
@@ -110,7 +96,12 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
11096 target_os = "aix"
11197) ) ]
11298#[ cfg_attr( target_family = "wasm" , allow( unused) ) ] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten)
113- pub unsafe fn register_dtor ( t : * mut u8 , dtor : unsafe extern "C" fn ( * mut u8 ) ) {
114- use crate :: sys_common:: thread_local_dtor:: register_dtor_fallback;
115- register_dtor_fallback ( t, dtor) ;
99+ pub fn activate ( ) {
100+ use crate :: sys_common:: thread_local_key:: StaticKey ;
101+
102+ static KEY : StaticKey = StaticKey :: new ( Some ( run_dtors) ) ;
103+
104+ unsafe {
105+ KEY . set ( ptr:: invalid_mut ( 1 ) ) ;
106+ }
116107}
0 commit comments