@@ -33,30 +33,57 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
33
33
register_dtor_fallback ( t, dtor) ;
34
34
}
35
35
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.
39
46
#[ cfg( target_os = "macos" ) ]
40
47
pub 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
+
41
67
extern {
42
68
fn _tlv_atexit ( dtor : unsafe extern fn ( * mut u8 ) ,
43
69
arg : * mut u8 ) ;
44
70
}
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
+ }
46
85
}
47
86
48
87
pub 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
62
89
}
0 commit comments