@@ -17,9 +17,6 @@ pub struct LazyKey {
1717 key : AtomicUsize ,
1818 /// Destructor for the TLS value.
1919 dtor : Option < unsafe extern "C" fn ( * mut u8 ) > ,
20- /// Next element of process-wide destructor list.
21- #[ cfg( not( target_thread_local) ) ]
22- next : atomic:: AtomicPtr < LazyKey > ,
2320}
2421
2522// Define a sentinel value that is likely not to be returned
@@ -34,12 +31,7 @@ const KEY_SENTVAL: usize = libc::PTHREAD_KEYS_MAX + 1;
3431
3532impl LazyKey {
3633 pub const fn new ( dtor : Option < unsafe extern "C" fn ( * mut u8 ) > ) -> LazyKey {
37- LazyKey {
38- key : atomic:: AtomicUsize :: new ( KEY_SENTVAL ) ,
39- dtor,
40- #[ cfg( not( target_thread_local) ) ]
41- next : atomic:: AtomicPtr :: new ( crate :: ptr:: null_mut ( ) ) ,
42- }
34+ LazyKey { key : atomic:: AtomicUsize :: new ( KEY_SENTVAL ) , dtor }
4335 }
4436
4537 #[ inline]
@@ -71,79 +63,109 @@ impl LazyKey {
7163 key2
7264 } ;
7365 rtassert ! ( key as usize != KEY_SENTVAL ) ;
74- match self . key . compare_exchange (
66+
67+ let final_key = match self . key . compare_exchange (
7568 KEY_SENTVAL ,
7669 key as usize ,
7770 Ordering :: Release ,
7871 Ordering :: Acquire ,
7972 ) {
8073 // The CAS succeeded, so we've created the actual key
81- Ok ( _) => {
82- #[ cfg( not( target_thread_local) ) ]
83- if self . dtor . is_some ( ) {
84- unsafe { register_dtor ( self ) } ;
85- }
86- key as usize
87- }
74+ Ok ( _) => key as usize ,
8875 // If someone beat us to the punch, use their key instead
8976 Err ( n) => unsafe {
9077 super :: destroy ( key) ;
9178 n
9279 } ,
80+ } ;
81+
82+ #[ cfg( not( target_thread_local) ) ]
83+ if self . dtor . is_some ( ) {
84+ unsafe { register_dtor ( self ) } ;
9385 }
86+
87+ final_key
9488 }
9589}
9690
9791/// POSIX does not run TLS destructors on process exit.
98- /// Thus we keep our own global list for that purpose.
92+ /// Thus we keep our own thread-local list for that purpose.
9993#[ cfg( not( target_thread_local) ) ]
100- static DTORS : atomic:: AtomicPtr < LazyKey > = atomic:: AtomicPtr :: new ( crate :: ptr:: null_mut ( ) ) ;
94+ fn lazy_keys ( ) -> & ' static crate :: cell:: RefCell < Vec < & ' static LazyKey > > {
95+ static KEY : atomic:: AtomicUsize = atomic:: AtomicUsize :: new ( KEY_SENTVAL ) ;
96+
97+ unsafe extern "C" fn drop_lazy_keys ( ptr : * mut u8 ) {
98+ let ptr = ptr as * mut crate :: cell:: RefCell < Vec < & ' static LazyKey > > ;
99+ if !ptr. is_null ( ) {
100+ drop ( unsafe { Box :: from_raw ( ptr) } ) ;
101+ }
102+ }
103+
104+ // Allocate a TLS key to store the thread local destructor list.
105+ let mut key = KEY . load ( Ordering :: Acquire ) as super :: Key ;
106+ if key == KEY_SENTVAL as _ {
107+ let new_key = super :: create ( Some ( drop_lazy_keys) ) ;
108+ match KEY . compare_exchange_weak (
109+ KEY_SENTVAL ,
110+ new_key as _ ,
111+ Ordering :: Release ,
112+ Ordering :: Acquire ,
113+ ) {
114+ Ok ( _) => key = new_key,
115+ Err ( other_key) => {
116+ unsafe { super :: destroy ( new_key) } ;
117+ key = other_key as _ ;
118+ }
119+ }
120+ }
121+
122+ // And allocate the list for this thread if necessary.
123+ let mut ptr = unsafe { super :: get ( key) as * const crate :: cell:: RefCell < Vec < & ' static LazyKey > > } ;
124+ if ptr. is_null ( ) {
125+ let list = Box :: new ( crate :: cell:: RefCell :: new ( Vec :: new ( ) ) ) ;
126+ ptr = Box :: into_raw ( list) ;
127+ unsafe { super :: set ( key, ptr as _ ) } ;
128+ }
129+
130+ unsafe { & * ptr }
131+ }
101132
102133/// Registers destructor to run at process exit.
103134#[ cfg( not( target_thread_local) ) ]
104- unsafe fn register_dtor ( key : & ' static LazyKey ) {
135+ unsafe fn register_dtor ( lazy_key : & ' static LazyKey ) {
105136 crate :: sys:: thread_local:: guard:: enable ( ) ;
106137
107- let this = <* const LazyKey >:: cast_mut ( key) ;
108- // Use acquire ordering to pass along the changes done by the previously
109- // registered keys when we store the new head with release ordering.
110- let mut head = DTORS . load ( Ordering :: Acquire ) ;
111- loop {
112- key. next . store ( head, Ordering :: Relaxed ) ;
113- match DTORS . compare_exchange_weak ( head, this, Ordering :: Release , Ordering :: Acquire ) {
114- Ok ( _) => break ,
115- Err ( new) => head = new,
116- }
117- }
138+ lazy_keys ( ) . borrow_mut ( ) . push ( lazy_key) ;
118139}
119140
120141/// Run destructors at process exit.
121142///
122143/// SAFETY: This will and must only be run by the destructor callback in [`guard`].
123144#[ cfg( not( target_thread_local) ) ]
124145pub unsafe fn run_dtors ( ) {
146+ let lazy_keys_cell = lazy_keys ( ) ;
147+ let mut lazy_keys = lazy_keys_cell. take ( ) ;
148+
125149 for _ in 0 ..5 {
126150 let mut any_run = false ;
127-
128- // Use acquire ordering to observe key initialization.
129- let mut cur = DTORS . load ( Ordering :: Acquire ) ;
130- while !cur. is_null ( ) {
131- let key = unsafe { ( * cur) . key . load ( Ordering :: Acquire ) } ;
132- let dtor = unsafe { ( * cur) . dtor . unwrap ( ) } ;
133- cur = unsafe { ( * cur) . next . load ( Ordering :: Relaxed ) } ;
134-
135- let ptr = unsafe { super :: get ( key as _ ) } ;
136- if !ptr. is_null ( ) {
137- unsafe {
138- super :: set ( key as _ , crate :: ptr:: null_mut ( ) ) ;
139- dtor ( ptr as * mut _ ) ;
151+ for lazy_key in & lazy_keys {
152+ if let Some ( dtor) = & lazy_key. dtor {
153+ let key = lazy_key. force ( ) ;
154+ let ptr = unsafe { super :: get ( key) } ;
155+ if !ptr. is_null ( ) {
156+ unsafe { dtor ( ptr) } ;
157+ unsafe { super :: set ( key, crate :: ptr:: null_mut ( ) ) } ;
140158 any_run = true ;
141159 }
142160 }
143161 }
144162
145- if !any_run {
163+ let mut new_lazy_keys = lazy_keys_cell. borrow_mut ( ) ;
164+
165+ if !any_run && new_lazy_keys. is_empty ( ) {
146166 break ;
147167 }
168+
169+ lazy_keys. extend ( new_lazy_keys. drain ( ..) ) ;
148170 }
149171}
0 commit comments