@@ -33,30 +33,57 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
3333 register_dtor_fallback ( t, dtor) ;
3434}
3535
36- // macOS's analog of the above linux function is this _tlv_atexit function.
37- // The disassembly of thread_local globals in C++ (at least produced by
38- // clang) will have this show up in the output.
36+ // This implementation is very similar to register_dtor_fallback in
37+ // sys_common/thread_local.rs. The main difference is that we want to hook into
38+ // macOS's analog of the above linux function, _tlv_atexit. OSX will run the
39+ // registered dtors before any TLS slots get freed, and when the main thread
40+ // exits.
41+ //
42+ // Unfortunately, calling _tlv_atexit while tls dtors are running is UB. The
43+ // workaround below is to register, via _tlv_atexit, a custom DTOR list once per
44+ // thread. thread_local dtors are pushed to the DTOR list without calling
45+ // _tlv_atexit.
3946#[ cfg( target_os = "macos" ) ]
4047pub unsafe fn register_dtor ( t : * mut u8 , dtor : unsafe extern fn ( * mut u8 ) ) {
48+ use cell:: Cell ;
49+ use ptr;
50+
51+ #[ thread_local]
52+ static REGISTERED : Cell < bool > = Cell :: new ( false ) ;
53+ if !REGISTERED . get ( ) {
54+ _tlv_atexit ( run_dtors, ptr:: null_mut ( ) ) ;
55+ REGISTERED . set ( true ) ;
56+ }
57+
58+ type List = Vec < ( * mut u8 , unsafe extern fn ( * mut u8 ) ) > ;
59+
60+ #[ thread_local]
61+ static DTORS : Cell < * mut List > = Cell :: new ( ptr:: null_mut ( ) ) ;
62+ if DTORS . get ( ) . is_null ( ) {
63+ let v: Box < List > = box Vec :: new ( ) ;
64+ DTORS . set ( Box :: into_raw ( v) ) ;
65+ }
66+
4167 extern {
4268 fn _tlv_atexit ( dtor : unsafe extern fn ( * mut u8 ) ,
4369 arg : * mut u8 ) ;
4470 }
45- _tlv_atexit ( dtor, t) ;
71+
72+ let list: & mut List = & mut * DTORS . get ( ) ;
73+ list. push ( ( t, dtor) ) ;
74+
75+ unsafe extern fn run_dtors ( _: * mut u8 ) {
76+ let mut ptr = DTORS . replace ( ptr:: null_mut ( ) ) ;
77+ while !ptr. is_null ( ) {
78+ let list = Box :: from_raw ( ptr) ;
79+ for ( ptr, dtor) in list. into_iter ( ) {
80+ dtor ( ptr) ;
81+ }
82+ ptr = DTORS . replace ( ptr:: null_mut ( ) ) ;
83+ }
84+ }
4685}
4786
4887pub fn requires_move_before_drop ( ) -> bool {
49- // The macOS implementation of TLS apparently had an odd aspect to it
50- // where the pointer we have may be overwritten while this destructor
51- // is running. Specifically if a TLS destructor re-accesses TLS it may
52- // trigger a re-initialization of all TLS variables, paving over at
53- // least some destroyed ones with initial values.
54- //
55- // This means that if we drop a TLS value in place on macOS that we could
56- // revert the value to its original state halfway through the
57- // destructor, which would be bad!
58- //
59- // Hence, we use `ptr::read` on macOS (to move to a "safe" location)
60- // instead of drop_in_place.
61- cfg ! ( target_os = "macos" )
88+ false
6289}
0 commit comments