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
2325// FIXME: The Rust compiler currently omits weakly function definitions (i.e.,
2426// __cxa_thread_atexit_impl) and its metadata from LLVM IR.
2527#[ no_sanitize( cfi, kcfi) ]
26- pub unsafe fn register_dtor ( t : * mut u8 , dtor : unsafe extern "C" fn ( * mut u8 ) ) {
28+ pub fn activate ( ) {
29+ use crate :: cell:: Cell ;
2730 use crate :: mem;
28- use crate :: sys_common:: thread_local_dtor :: register_dtor_fallback ;
31+ use crate :: sys_common:: thread_local_key :: StaticKey ;
2932
3033 /// This is necessary because the __cxa_thread_atexit_impl implementation
3134 /// std links to by default may be a C or C++ implementation that was not
@@ -50,64 +53,47 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
5053 > ;
5154 }
5255
53- if let Some ( f) = __cxa_thread_atexit_impl {
54- unsafe {
55- f (
56- mem:: transmute :: <
57- unsafe extern "C" fn ( * mut u8 ) ,
58- unsafe extern "C" fn ( * mut libc:: c_void ) ,
59- > ( dtor) ,
60- t. cast ( ) ,
61- & __dso_handle as * const _ as * mut _ ,
62- ) ;
56+ unsafe {
57+ if let Some ( atexit) = __cxa_thread_atexit_impl {
58+ #[ thread_local]
59+ static REGISTERED : Cell < bool > = Cell :: new ( false ) ;
60+ if !REGISTERED . get ( ) {
61+ atexit (
62+ mem:: transmute :: <
63+ unsafe extern "C" fn ( * mut u8 ) ,
64+ unsafe extern "C" fn ( * mut libc:: c_void ) ,
65+ > ( run_dtors) ,
66+ ptr:: null_mut ( ) ,
67+ & __dso_handle as * const _ as * mut _ ,
68+ ) ;
69+ REGISTERED . set ( true ) ;
70+ }
71+ } else {
72+ static KEY : StaticKey = StaticKey :: new ( Some ( run_dtors) ) ;
73+
74+ KEY . set ( ptr:: invalid_mut ( 1 ) ) ;
6375 }
64- return ;
6576 }
66- register_dtor_fallback ( t, dtor) ;
6777}
6878
69- // This implementation is very similar to register_dtor_fallback in
70- // sys_common/thread_local.rs. The main difference is that we want to hook into
71- // macOS's analog of the above linux function, _tlv_atexit. OSX will run the
72- // registered dtors before any TLS slots get freed, and when the main thread
79+ // We hook into macOS's analog of the above linux function, _tlv_atexit. OSX
80+ // will run `run_dtors` before any TLS slots get freed, and when the main thread
7381// exits.
74- //
75- // Unfortunately, calling _tlv_atexit while tls dtors are running is UB. The
76- // workaround below is to register, via _tlv_atexit, a custom DTOR list once per
77- // thread. thread_local dtors are pushed to the DTOR list without calling
78- // _tlv_atexit.
7982#[ cfg( any( target_os = "macos" , target_os = "ios" , target_os = "watchos" , target_os = "tvos" ) ) ]
80- pub unsafe fn register_dtor ( t : * mut u8 , dtor : unsafe extern "C" fn ( * mut u8 ) ) {
81- use crate :: cell:: { Cell , RefCell } ;
82- use crate :: ptr;
83-
84- #[ thread_local]
85- static REGISTERED : Cell < bool > = Cell :: new ( false ) ;
86-
87- #[ thread_local]
88- static DTORS : RefCell < Vec < ( * mut u8 , unsafe extern "C" fn ( * mut u8 ) ) > > = RefCell :: new ( Vec :: new ( ) ) ;
89-
90- if !REGISTERED . get ( ) {
91- _tlv_atexit ( run_dtors, ptr:: null_mut ( ) ) ;
92- REGISTERED . set ( true ) ;
93- }
83+ pub fn activate ( ) {
84+ use crate :: cell:: Cell ;
9485
9586 extern "C" {
9687 fn _tlv_atexit ( dtor : unsafe extern "C" fn ( * mut u8 ) , arg : * mut u8 ) ;
9788 }
9889
99- match DTORS . try_borrow_mut ( ) {
100- Ok ( mut dtors) => dtors. push ( ( t, dtor) ) ,
101- Err ( _) => rtabort ! ( "global allocator may not use TLS" ) ,
102- }
90+ #[ thread_local]
91+ static REGISTERED : Cell < bool > = Cell :: new ( false ) ;
10392
104- unsafe extern "C" fn run_dtors ( _: * mut u8 ) {
105- let mut list = DTORS . take ( ) ;
106- while !list. is_empty ( ) {
107- for ( ptr, dtor) in list {
108- dtor ( ptr) ;
109- }
110- list = DTORS . take ( ) ;
93+ if !REGISTERED . get ( ) {
94+ unsafe {
95+ _tlv_atexit ( run_dtors, ptr:: null_mut ( ) ) ;
96+ REGISTERED . set ( true ) ;
11197 }
11298 }
11399}
@@ -120,7 +106,12 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
120106 target_os = "freebsd" ,
121107) ) ]
122108#[ cfg_attr( target_family = "wasm" , allow( unused) ) ] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten)
123- pub unsafe fn register_dtor ( t : * mut u8 , dtor : unsafe extern "C" fn ( * mut u8 ) ) {
124- use crate :: sys_common:: thread_local_dtor:: register_dtor_fallback;
125- register_dtor_fallback ( t, dtor) ;
109+ pub fn activate ( ) {
110+ use crate :: sys_common:: thread_local_key:: StaticKey ;
111+
112+ static KEY : StaticKey = StaticKey :: new ( Some ( run_dtors) ) ;
113+
114+ unsafe {
115+ KEY . set ( ptr:: invalid_mut ( 1 ) ) ;
116+ }
126117}
0 commit comments