]>
git.proxmox.com Git - rustc.git/blob - src/libstd/sys/windows/thread_local.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
13 use libc
::types
::os
::arch
::extra
::{DWORD, LPVOID, BOOL}
;
17 use sys_common
::mutex
::Mutex
;
20 pub type Dtor
= unsafe extern fn(*mut u8);
22 // Turns out, like pretty much everything, Windows is pretty close the
23 // functionality that Unix provides, but slightly different! In the case of
24 // TLS, Windows does not provide an API to provide a destructor for a TLS
25 // variable. This ends up being pretty crucial to this implementation, so we
26 // need a way around this.
28 // The solution here ended up being a little obscure, but fear not, the
29 // internet has informed me [1][2] that this solution is not unique (no way
30 // I could have thought of it as well!). The key idea is to insert some hook
31 // somewhere to run arbitrary code on thread termination. With this in place
32 // we'll be able to run anything we like, including all TLS destructors!
34 // To accomplish this feat, we perform a number of threads, all contained
35 // within this module:
37 // * All TLS destructors are tracked by *us*, not the windows runtime. This
38 // means that we have a global list of destructors for each TLS key that
40 // * When a TLS key is destroyed, we're sure to remove it from the dtor list
42 // * When a thread exits, we run over the entire list and run dtors for all
43 // non-null keys. This attempts to match Unix semantics in this regard.
45 // This ends up having the overhead of using a global list, having some
46 // locks here and there, and in general just adding some more code bloat. We
47 // attempt to optimize runtime by forgetting keys that don't have
48 // destructors, but this only gets us so far.
50 // For more details and nitty-gritty, see the code sections below!
52 // [1]: http://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way
53 // [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base
54 // /threading/thread_local_storage_win.cc#L42
56 // NB these are specifically not types from `std::sync` as they currently rely
57 // on poisoning and this module needs to operate at a lower level than requiring
58 // the thread infrastructure to be in place (useful on the borders of
59 // initialization/destruction).
60 static DTOR_LOCK
: Mutex
= Mutex
::new();
61 static mut DTORS
: *mut Vec
<(Key
, Dtor
)> = 0 as *mut _
;
63 // -------------------------------------------------------------------------
66 // This section is just raw bindings to the native functions that Windows
67 // provides, There's a few extra calls to deal with destructors.
70 pub unsafe fn create(dtor
: Option
<Dtor
>) -> Key
{
71 const TLS_OUT_OF_INDEXES
: DWORD
= 0xFFFFFFFF;
73 assert
!(key
!= TLS_OUT_OF_INDEXES
);
75 Some(f
) => register_dtor(key
, f
),
82 pub unsafe fn set(key
: Key
, value
: *mut u8) {
83 let r
= TlsSetValue(key
, value
as LPVOID
);
84 debug_assert
!(r
!= 0);
88 pub unsafe fn get(key
: Key
) -> *mut u8 {
89 TlsGetValue(key
) as *mut u8
93 pub unsafe fn destroy(key
: Key
) {
94 if unregister_dtor(key
) {
95 // FIXME: Currently if a key has a destructor associated with it we
96 // can't actually ever unregister it. If we were to
97 // unregister it, then any key destruction would have to be
98 // serialized with respect to actually running destructors.
100 // We want to avoid a race where right before run_dtors runs
101 // some destructors TlsFree is called. Allowing the call to
102 // TlsFree would imply that the caller understands that *all
103 // known threads* are not exiting, which is quite a difficult
106 // For now we just leak all keys with dtors to "fix" this.
107 // Note that source [2] above shows precedent for this sort
110 let r
= TlsFree(key
);
111 debug_assert
!(r
!= 0);
116 fn TlsAlloc() -> DWORD
;
117 fn TlsFree(dwTlsIndex
: DWORD
) -> BOOL
;
118 fn TlsGetValue(dwTlsIndex
: DWORD
) -> LPVOID
;
119 fn TlsSetValue(dwTlsIndex
: DWORD
, lpTlsvalue
: LPVOID
) -> BOOL
;
122 // -------------------------------------------------------------------------
125 // These functions are associated with registering and unregistering
126 // destructors. They're pretty simple, they just push onto a vector and scan
127 // a vector currently.
129 // FIXME: This could probably be at least a little faster with a BTree.
131 unsafe fn init_dtors() {
132 if !DTORS
.is_null() { return }
134 let dtors
= box Vec
::<(Key
, Dtor
)>::new();
136 let res
= rt
::at_exit(move|| {
140 Box
::from_raw(dtors
);
141 assert
!(DTORS
as usize == 1); // can't re-init after destructing
145 DTORS
= Box
::into_raw(dtors
);
151 unsafe fn register_dtor(key
: Key
, dtor
: Dtor
) {
154 assert
!(DTORS
as usize != 0);
155 assert
!(DTORS
as usize != 1,
156 "cannot create new TLS keys after the main thread has exited");
157 (*DTORS
).push((key
, dtor
));
161 unsafe fn unregister_dtor(key
: Key
) -> bool
{
164 assert
!(DTORS
as usize != 0);
165 assert
!(DTORS
as usize != 1,
166 "cannot unregister destructors after the main thread has exited");
168 let dtors
= &mut *DTORS
;
169 let before
= dtors
.len();
170 dtors
.retain(|&(k
, _
)| k
!= key
);
171 dtors
.len() != before
177 // -------------------------------------------------------------------------
178 // Where the Magic (TM) Happens
180 // If you're looking at this code, and wondering "what is this doing?",
181 // you're not alone! I'll try to break this down step by step:
183 // # What's up with CRT$XLB?
185 // For anything about TLS destructors to work on Windows, we have to be able
186 // to run *something* when a thread exits. To do so, we place a very special
187 // static in a very special location. If this is encoded in just the right
188 // way, the kernel's loader is apparently nice enough to run some function
189 // of ours whenever a thread exits! How nice of the kernel!
191 // Lots of detailed information can be found in source [1] above, but the
192 // gist of it is that this is leveraging a feature of Microsoft's PE format
193 // (executable format) which is not actually used by any compilers today.
194 // This apparently translates to any callbacks in the ".CRT$XLB" section
195 // being run on certain events.
197 // So after all that, we use the compiler's #[link_section] feature to place
198 // a callback pointer into the magic section so it ends up being called.
200 // # What's up with this callback?
202 // The callback specified receives a number of parameters from... someone!
203 // (the kernel? the runtime? I'm not quite sure!) There are a few events that
204 // this gets invoked for, but we're currently only interested on when a
205 // thread or a process "detaches" (exits). The process part happens for the
206 // last thread and the thread part happens for any normal thread.
208 // # Ok, what's up with running all these destructors?
210 // This will likely need to be improved over time, but this function
211 // attempts a "poor man's" destructor callback system. To do this we clone a
212 // local copy of the dtor list to start out with. This is our fudgy attempt
213 // to not hold the lock while destructors run and not worry about the list
214 // changing while we're looking at it.
216 // Once we've got a list of what to run, we iterate over all keys, check
217 // their values, and then run destructors if the values turn out to be non
218 // null (setting them to null just beforehand). We do this a few times in a
219 // loop to basically match Unix semantics. If we don't reach a fixed point
220 // after a short while then we just inevitably leak something most likely.
222 // # The article mentions crazy stuff about "/INCLUDE"?
224 // It sure does! This seems to work for now, so maybe we'll just run into
225 // that if we start linking with msvc?
227 #[link_section = ".CRT$XLB"]
228 #[linkage = "external"]
230 pub static p_thread_callback
: unsafe extern "system" fn(LPVOID
, DWORD
,
235 unsafe extern "system" fn on_tls_callback(h
: LPVOID
,
238 const DLL_THREAD_DETACH
: DWORD
= 3;
239 const DLL_PROCESS_DETACH
: DWORD
= 0;
240 if dwReason
== DLL_THREAD_DETACH
|| dwReason
== DLL_PROCESS_DETACH
{
245 #[allow(dead_code)] // actually called above
246 unsafe fn run_dtors() {
247 let mut any_run
= true;
249 if !any_run { break }
253 let ret
= if DTORS
as usize <= 1 {
256 (*DTORS
).iter().map(|s
| *s
).collect()
261 for &(key
, dtor
) in &dtors
{
262 let ptr
= TlsGetValue(key
);
264 TlsSetValue(key
, ptr
::null_mut());