1
+ #![ cfg( not( target_thread_local) ) ]
2
+
1
3
use crate :: cell:: UnsafeCell ;
2
4
use crate :: ptr;
3
5
use crate :: sync:: atomic:: {
4
- AtomicBool , AtomicPtr , AtomicU32 ,
6
+ AtomicPtr , AtomicU32 ,
5
7
Ordering :: { AcqRel , Acquire , Relaxed , Release } ,
6
8
} ;
7
9
use crate :: sys:: c;
8
10
9
11
#[ cfg( test) ]
10
12
mod tests;
11
13
12
- /// An optimization hint. The compiler is often smart enough to know if an atomic
13
- /// is never set and can remove dead code based on that fact.
14
- static HAS_DTORS : AtomicBool = AtomicBool :: new ( false ) ;
15
-
16
- // Using a per-thread list avoids the problems in synchronizing global state.
17
- #[ thread_local]
18
- #[ cfg( target_thread_local) ]
19
- static mut DESTRUCTORS : Vec < ( * mut u8 , unsafe extern "C" fn ( * mut u8 ) ) > = Vec :: new ( ) ;
20
-
21
- // Ensure this can never be inlined because otherwise this may break in dylibs.
22
- // See #44391.
23
- #[ inline( never) ]
24
- #[ cfg( target_thread_local) ]
25
- pub unsafe fn register_keyless_dtor ( t : * mut u8 , dtor : unsafe extern "C" fn ( * mut u8 ) ) {
26
- DESTRUCTORS . push ( ( t, dtor) ) ;
27
- HAS_DTORS . store ( true , Relaxed ) ;
28
- }
29
-
30
- #[ inline( never) ] // See comment above
31
- #[ cfg( target_thread_local) ]
32
- /// Runs destructors. This should not be called until thread exit.
33
- unsafe fn run_keyless_dtors ( ) {
34
- // Drop all the destructors.
35
- //
36
- // Note: While this is potentially an infinite loop, it *should* be
37
- // the case that this loop always terminates because we provide the
38
- // guarantee that a TLS key cannot be set after it is flagged for
39
- // destruction.
40
- while let Some ( ( ptr, dtor) ) = DESTRUCTORS . pop ( ) {
41
- ( dtor) ( ptr) ;
42
- }
43
- // We're done so free the memory.
44
- DESTRUCTORS = Vec :: new ( ) ;
45
- }
46
-
47
14
type Key = c:: DWORD ;
48
15
type Dtor = unsafe extern "C" fn ( * mut u8 ) ;
49
16
50
- // Turns out, like pretty much everything, Windows is pretty close the
51
- // functionality that Unix provides, but slightly different! In the case of
52
- // TLS, Windows does not provide an API to provide a destructor for a TLS
53
- // variable. This ends up being pretty crucial to this implementation, so we
54
- // need a way around this.
55
- //
56
- // The solution here ended up being a little obscure, but fear not, the
57
- // internet has informed me [1][2] that this solution is not unique (no way
58
- // I could have thought of it as well!). The key idea is to insert some hook
59
- // somewhere to run arbitrary code on thread termination. With this in place
60
- // we'll be able to run anything we like, including all TLS destructors!
61
- //
62
- // To accomplish this feat, we perform a number of threads, all contained
63
- // within this module:
64
- //
65
- // * All TLS destructors are tracked by *us*, not the Windows runtime. This
66
- // means that we have a global list of destructors for each TLS key that
67
- // we know about.
68
- // * When a thread exits, we run over the entire list and run dtors for all
69
- // non-null keys. This attempts to match Unix semantics in this regard.
70
- //
71
- // For more details and nitty-gritty, see the code sections below!
72
- //
73
- // [1]: https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way
74
- // [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base/threading/thread_local_storage_win.cc#L42
75
-
76
17
pub struct StaticKey {
77
18
/// The key value shifted up by one. Since TLS_OUT_OF_INDEXES == DWORD::MAX
78
19
/// is not a valid key value, this allows us to use zero as sentinel value
@@ -204,41 +145,10 @@ unsafe fn register_dtor(key: &'static StaticKey) {
204
145
Err ( new) => head = new,
205
146
}
206
147
}
207
- HAS_DTORS . store ( true , Release ) ;
148
+ super :: thread_local_guard :: activate ( ) ;
208
149
}
209
150
210
- // -------------------------------------------------------------------------
211
- // Where the Magic (TM) Happens
212
- //
213
- // If you're looking at this code, and wondering "what is this doing?",
214
- // you're not alone! I'll try to break this down step by step:
215
- //
216
- // # What's up with CRT$XLB?
217
- //
218
- // For anything about TLS destructors to work on Windows, we have to be able
219
- // to run *something* when a thread exits. To do so, we place a very special
220
- // static in a very special location. If this is encoded in just the right
221
- // way, the kernel's loader is apparently nice enough to run some function
222
- // of ours whenever a thread exits! How nice of the kernel!
223
- //
224
- // Lots of detailed information can be found in source [1] above, but the
225
- // gist of it is that this is leveraging a feature of Microsoft's PE format
226
- // (executable format) which is not actually used by any compilers today.
227
- // This apparently translates to any callbacks in the ".CRT$XLB" section
228
- // being run on certain events.
229
- //
230
- // So after all that, we use the compiler's #[link_section] feature to place
231
- // a callback pointer into the magic section so it ends up being called.
232
- //
233
- // # What's up with this callback?
234
- //
235
- // The callback specified receives a number of parameters from... someone!
236
- // (the kernel? the runtime? I'm not quite sure!) There are a few events that
237
- // this gets invoked for, but we're currently only interested on when a
238
- // thread or a process "detaches" (exits). The process part happens for the
239
- // last thread and the thread part happens for any normal thread.
240
- //
241
- // # Ok, what's up with running all these destructors?
151
+ // What's up with running all these destructors?
242
152
//
243
153
// This will likely need to be improved over time, but this function
244
154
// attempts a "poor man's" destructor callback system. Once we've got a list
@@ -247,63 +157,7 @@ unsafe fn register_dtor(key: &'static StaticKey) {
247
157
// beforehand). We do this a few times in a loop to basically match Unix
248
158
// semantics. If we don't reach a fixed point after a short while then we just
249
159
// inevitably leak something most likely.
250
- //
251
- // # The article mentions weird stuff about "/INCLUDE"?
252
- //
253
- // It sure does! Specifically we're talking about this quote:
254
- //
255
- // The Microsoft run-time library facilitates this process by defining a
256
- // memory image of the TLS Directory and giving it the special name
257
- // “__tls_used” (Intel x86 platforms) or “_tls_used” (other platforms). The
258
- // linker looks for this memory image and uses the data there to create the
259
- // TLS Directory. Other compilers that support TLS and work with the
260
- // Microsoft linker must use this same technique.
261
- //
262
- // Basically what this means is that if we want support for our TLS
263
- // destructors/our hook being called then we need to make sure the linker does
264
- // not omit this symbol. Otherwise it will omit it and our callback won't be
265
- // wired up.
266
- //
267
- // We don't actually use the `/INCLUDE` linker flag here like the article
268
- // mentions because the Rust compiler doesn't propagate linker flags, but
269
- // instead we use a shim function which performs a volatile 1-byte load from
270
- // the address of the symbol to ensure it sticks around.
271
-
272
- #[ link_section = ".CRT$XLB" ]
273
- #[ allow( dead_code, unused_variables) ]
274
- #[ used] // we don't want LLVM eliminating this symbol for any reason, and
275
- // when the symbol makes it to the linker the linker will take over
276
- pub static p_thread_callback: unsafe extern "system" fn ( c:: LPVOID , c:: DWORD , c:: LPVOID ) =
277
- on_tls_callback;
278
-
279
- #[ allow( dead_code, unused_variables) ]
280
- unsafe extern "system" fn on_tls_callback ( h : c:: LPVOID , dwReason : c:: DWORD , pv : c:: LPVOID ) {
281
- if !HAS_DTORS . load ( Acquire ) {
282
- return ;
283
- }
284
- if dwReason == c:: DLL_THREAD_DETACH || dwReason == c:: DLL_PROCESS_DETACH {
285
- #[ cfg( not( target_thread_local) ) ]
286
- run_dtors ( ) ;
287
- #[ cfg( target_thread_local) ]
288
- run_keyless_dtors ( ) ;
289
- }
290
-
291
- // See comments above for what this is doing. Note that we don't need this
292
- // trickery on GNU windows, just on MSVC.
293
- reference_tls_used ( ) ;
294
- #[ cfg( target_env = "msvc" ) ]
295
- unsafe fn reference_tls_used ( ) {
296
- extern "C" {
297
- static _tls_used: u8 ;
298
- }
299
- crate :: intrinsics:: volatile_load ( & _tls_used) ;
300
- }
301
- #[ cfg( not( target_env = "msvc" ) ) ]
302
- unsafe fn reference_tls_used ( ) { }
303
- }
304
-
305
- #[ allow( dead_code) ] // actually called below
306
- unsafe fn run_dtors ( ) {
160
+ pub ( super ) unsafe fn run_dtors ( _ptr : * mut u8 ) {
307
161
for _ in 0 ..5 {
308
162
let mut any_run = false ;
309
163
0 commit comments