1
+ //! Ensures that thread-local destructors are run on thread exit.
2
+
1
3
#![ cfg( target_thread_local) ]
2
4
#![ unstable( feature = "thread_local_internals" , issue = "none" ) ]
3
5
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 ;
6
8
7
9
// Since what appears to be glibc 2.18 this symbol has been shipped which
8
10
// GCC and clang both use to invoke destructors in thread_local globals, so
23
25
// FIXME: The Rust compiler currently omits weakly function definitions (i.e.,
24
26
// __cxa_thread_atexit_impl) and its metadata from LLVM IR.
25
27
#[ 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 ;
27
30
use crate :: mem;
28
- use crate :: sys_common:: thread_local_dtor :: register_dtor_fallback ;
31
+ use crate :: sys_common:: thread_local_key :: StaticKey ;
29
32
30
33
/// This is necessary because the __cxa_thread_atexit_impl implementation
31
34
/// 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)) {
50
53
> ;
51
54
}
52
55
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 ) ) ;
63
75
}
64
- return ;
65
76
}
66
- register_dtor_fallback ( t, dtor) ;
67
77
}
68
78
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
73
81
// 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.
79
82
#[ 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 ;
94
85
95
86
extern "C" {
96
87
fn _tlv_atexit ( dtor : unsafe extern "C" fn ( * mut u8 ) , arg : * mut u8 ) ;
97
88
}
98
89
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 ) ;
103
92
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 ) ;
111
97
}
112
98
}
113
99
}
@@ -120,7 +106,12 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
120
106
target_os = "freebsd" ,
121
107
) ) ]
122
108
#[ 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
+ }
126
117
}
0 commit comments