1
1
use super :: lazy:: LazyKeyInner ;
2
- use crate :: cell:: Cell ;
3
- use crate :: sys:: thread_local_dtor:: register_dtor;
2
+ use crate :: cell:: { Cell , RefCell } ;
4
3
use crate :: { fmt, mem, panic} ;
5
4
6
5
#[ doc( hidden) ]
@@ -39,13 +38,11 @@ pub macro thread_local_inner {
39
38
40
39
// Safety: Performs `drop_in_place(ptr as *mut $t)`, and requires
41
40
// all that comes with it.
42
- unsafe extern "C" fn destroy ( ptr : * mut $crate:: primitive:: u8 ) {
43
- $crate:: thread:: local_impl:: abort_on_dtor_unwind ( || {
44
- let old_state = STATE . replace ( 2 ) ;
45
- $crate:: debug_assert_eq!( old_state, 1 ) ;
46
- // Safety: safety requirement is passed on to caller.
47
- unsafe { $crate:: ptr:: drop_in_place ( ptr. cast :: < $t> ( ) ) ; }
48
- } ) ;
41
+ unsafe fn destroy ( ptr : * mut $crate:: primitive:: u8 ) {
42
+ let old_state = STATE . replace ( 2 ) ;
43
+ $crate:: debug_assert_eq!( old_state, 1 ) ;
44
+ // Safety: safety requirement is passed on to caller.
45
+ unsafe { $crate:: ptr:: drop_in_place ( ptr. cast :: < $t> ( ) ) ; }
49
46
}
50
47
51
48
unsafe {
@@ -154,8 +151,8 @@ impl<T> Key<T> {
154
151
155
152
// note that this is just a publicly-callable function only for the
156
153
// const-initialized form of thread locals, basically a way to call the
157
- // free `register_dtor` function defined elsewhere in std .
158
- pub unsafe fn register_dtor ( a : * mut u8 , dtor : unsafe extern "C" fn ( * mut u8 ) ) {
154
+ // free `register_dtor` function.
155
+ pub unsafe fn register_dtor ( a : * mut u8 , dtor : unsafe fn ( * mut u8 ) ) {
159
156
unsafe {
160
157
register_dtor ( a, dtor) ;
161
158
}
@@ -219,7 +216,7 @@ impl<T> Key<T> {
219
216
}
220
217
}
221
218
222
- unsafe extern "C" fn destroy_value < T > ( ptr : * mut u8 ) {
219
+ unsafe fn destroy_value < T > ( ptr : * mut u8 ) {
223
220
let ptr = ptr as * mut Key < T > ;
224
221
225
222
// SAFETY:
@@ -232,14 +229,66 @@ unsafe extern "C" fn destroy_value<T>(ptr: *mut u8) {
232
229
// `Option<T>` to `None`, and `dtor_state` to `RunningOrHasRun`. This
233
230
// causes future calls to `get` to run `try_initialize_drop` again,
234
231
// which will now fail, and return `None`.
235
- //
236
- // Wrap the call in a catch to ensure unwinding is caught in the event
237
- // a panic takes place in a destructor.
238
- if let Err ( _) = panic:: catch_unwind ( panic:: AssertUnwindSafe ( || unsafe {
232
+ unsafe {
239
233
let value = ( * ptr) . inner . take ( ) ;
240
234
( * ptr) . dtor_state . set ( DtorState :: RunningOrHasRun ) ;
241
235
drop ( value) ;
242
- } ) ) {
243
- rtabort ! ( "thread local panicked on drop" ) ;
236
+ }
237
+ }
238
+
239
+ #[ thread_local]
240
+ static DTORS : RefCell < Vec < ( * mut u8 , unsafe fn ( * mut u8 ) ) > > = RefCell :: new ( Vec :: new ( ) ) ;
241
+
242
+ // Ensure this can never be inlined on Windows because otherwise this may break
243
+ // in dylibs. See #44391.
244
+ #[ cfg_attr( windows, inline( never) ) ]
245
+ unsafe fn register_dtor ( t : * mut u8 , dtor : unsafe fn ( * mut u8 ) ) {
246
+ // Ensure that destructors are run on thread exit.
247
+ crate :: sys:: thread_local_guard:: activate ( ) ;
248
+
249
+ let mut dtors = match DTORS . try_borrow_mut ( ) {
250
+ Ok ( dtors) => dtors,
251
+ // The only place this function can be called reentrantly is inside the
252
+ // heap allocator. This is currently forbidden.
253
+ Err ( _) => rtabort ! ( "the global allocator may not register TLS destructors" ) ,
254
+ } ;
255
+ dtors. push ( ( t, dtor) ) ;
256
+ }
257
+
258
+ /// Called by the platform on thread exit to run all registered destructors.
259
+ /// The signature was chosen so that this function may be passed as a callback
260
+ /// to platform functions. The argument is ignored.
261
+ ///
262
+ /// # Safety
263
+ /// May only be called on thread exit. In particular, no thread locals may
264
+ /// currently be referenced.
265
+ pub unsafe extern "C" fn run_dtors ( _unused : * mut u8 ) {
266
+ // This function must not unwind. This is ensured by the `extern "C"` ABI,
267
+ // but by catching the unwind, we can print a more helpful message.
268
+
269
+ match panic:: catch_unwind ( || {
270
+ let dtors = & DTORS ;
271
+
272
+ loop {
273
+ // Ensure that the `RefMut` guard is not held while the destructor is
274
+ // executed to allow initializing TLS variables in destructors.
275
+ let ( t, dtor) = {
276
+ let mut dtors = dtors. borrow_mut ( ) ;
277
+ match dtors. pop ( ) {
278
+ Some ( entry) => entry,
279
+ None => break ,
280
+ }
281
+ } ;
282
+
283
+ unsafe {
284
+ ( dtor) ( t) ;
285
+ }
286
+ }
287
+
288
+ // All destructors were run, deallocate the list.
289
+ drop ( dtors. replace ( Vec :: new ( ) ) ) ;
290
+ } ) {
291
+ Ok ( ( ) ) => { }
292
+ Err ( _) => rtabort ! ( "thread local panicked on drop" ) ,
244
293
}
245
294
}
0 commit comments