]> git.proxmox.com Git - rustc.git/blame - src/libstd/sys/windows/thread_local.rs
Imported Upstream version 1.6.0+dfsg1
[rustc.git] / src / libstd / sys / windows / thread_local.rs
CommitLineData
1a4d82fc
JJ
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.
4//
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.
10
11use prelude::v1::*;
12
85aaf69f 13use ptr;
92a42be0 14use sys::c;
62682a34 15use sys_common::mutex::Mutex;
92a42be0 16use sys_common;
1a4d82fc 17
92a42be0 18pub type Key = c::DWORD;
1a4d82fc
JJ
19pub type Dtor = unsafe extern fn(*mut u8);
20
21// Turns out, like pretty much everything, Windows is pretty close the
22// functionality that Unix provides, but slightly different! In the case of
23// TLS, Windows does not provide an API to provide a destructor for a TLS
24// variable. This ends up being pretty crucial to this implementation, so we
25// need a way around this.
26//
27// The solution here ended up being a little obscure, but fear not, the
28// internet has informed me [1][2] that this solution is not unique (no way
29// I could have thought of it as well!). The key idea is to insert some hook
30// somewhere to run arbitrary code on thread termination. With this in place
31// we'll be able to run anything we like, including all TLS destructors!
32//
bd371182 33// To accomplish this feat, we perform a number of threads, all contained
1a4d82fc
JJ
34// within this module:
35//
36// * All TLS destructors are tracked by *us*, not the windows runtime. This
37// means that we have a global list of destructors for each TLS key that
38// we know about.
39// * When a TLS key is destroyed, we're sure to remove it from the dtor list
40// if it's in there.
41// * When a thread exits, we run over the entire list and run dtors for all
42// non-null keys. This attempts to match Unix semantics in this regard.
43//
44// This ends up having the overhead of using a global list, having some
45// locks here and there, and in general just adding some more code bloat. We
46// attempt to optimize runtime by forgetting keys that don't have
47// destructors, but this only gets us so far.
48//
49// For more details and nitty-gritty, see the code sections below!
50//
51// [1]: http://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way
52// [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base
53// /threading/thread_local_storage_win.cc#L42
54
55// NB these are specifically not types from `std::sync` as they currently rely
56// on poisoning and this module needs to operate at a lower level than requiring
57// the thread infrastructure to be in place (useful on the borders of
58// initialization/destruction).
62682a34 59static DTOR_LOCK: Mutex = Mutex::new();
e9174d1e 60static mut DTORS: *mut Vec<(Key, Dtor)> = ptr::null_mut();
1a4d82fc
JJ
61
62// -------------------------------------------------------------------------
63// Native bindings
64//
65// This section is just raw bindings to the native functions that Windows
66// provides, There's a few extra calls to deal with destructors.
67
68#[inline]
69pub unsafe fn create(dtor: Option<Dtor>) -> Key {
92a42be0
SL
70 let key = c::TlsAlloc();
71 assert!(key != c::TLS_OUT_OF_INDEXES);
1a4d82fc
JJ
72 match dtor {
73 Some(f) => register_dtor(key, f),
74 None => {}
75 }
76 return key;
77}
78
79#[inline]
80pub unsafe fn set(key: Key, value: *mut u8) {
92a42be0 81 let r = c::TlsSetValue(key, value as c::LPVOID);
1a4d82fc
JJ
82 debug_assert!(r != 0);
83}
84
85#[inline]
86pub unsafe fn get(key: Key) -> *mut u8 {
92a42be0 87 c::TlsGetValue(key) as *mut u8
1a4d82fc
JJ
88}
89
90#[inline]
91pub unsafe fn destroy(key: Key) {
92 if unregister_dtor(key) {
93 // FIXME: Currently if a key has a destructor associated with it we
94 // can't actually ever unregister it. If we were to
95 // unregister it, then any key destruction would have to be
96 // serialized with respect to actually running destructors.
97 //
98 // We want to avoid a race where right before run_dtors runs
99 // some destructors TlsFree is called. Allowing the call to
100 // TlsFree would imply that the caller understands that *all
101 // known threads* are not exiting, which is quite a difficult
102 // thing to know!
103 //
104 // For now we just leak all keys with dtors to "fix" this.
105 // Note that source [2] above shows precedent for this sort
106 // of strategy.
107 } else {
92a42be0 108 let r = c::TlsFree(key);
1a4d82fc
JJ
109 debug_assert!(r != 0);
110 }
111}
112
1a4d82fc
JJ
113// -------------------------------------------------------------------------
114// Dtor registration
115//
116// These functions are associated with registering and unregistering
117// destructors. They're pretty simple, they just push onto a vector and scan
118// a vector currently.
119//
120// FIXME: This could probably be at least a little faster with a BTree.
121
122unsafe fn init_dtors() {
123 if !DTORS.is_null() { return }
124
125 let dtors = box Vec::<(Key, Dtor)>::new();
1a4d82fc 126
e9174d1e 127 let res = sys_common::at_exit(move|| {
1a4d82fc
JJ
128 DTOR_LOCK.lock();
129 let dtors = DTORS;
c34b1796
AL
130 DTORS = 1 as *mut _;
131 Box::from_raw(dtors);
132 assert!(DTORS as usize == 1); // can't re-init after destructing
1a4d82fc
JJ
133 DTOR_LOCK.unlock();
134 });
c34b1796 135 if res.is_ok() {
62682a34 136 DTORS = Box::into_raw(dtors);
c34b1796
AL
137 } else {
138 DTORS = 1 as *mut _;
139 }
1a4d82fc
JJ
140}
141
142unsafe fn register_dtor(key: Key, dtor: Dtor) {
143 DTOR_LOCK.lock();
144 init_dtors();
c34b1796
AL
145 assert!(DTORS as usize != 0);
146 assert!(DTORS as usize != 1,
147 "cannot create new TLS keys after the main thread has exited");
1a4d82fc
JJ
148 (*DTORS).push((key, dtor));
149 DTOR_LOCK.unlock();
150}
151
152unsafe fn unregister_dtor(key: Key) -> bool {
153 DTOR_LOCK.lock();
154 init_dtors();
c34b1796
AL
155 assert!(DTORS as usize != 0);
156 assert!(DTORS as usize != 1,
157 "cannot unregister destructors after the main thread has exited");
1a4d82fc
JJ
158 let ret = {
159 let dtors = &mut *DTORS;
160 let before = dtors.len();
161 dtors.retain(|&(k, _)| k != key);
162 dtors.len() != before
163 };
164 DTOR_LOCK.unlock();
165 ret
166}
167
168// -------------------------------------------------------------------------
169// Where the Magic (TM) Happens
170//
171// If you're looking at this code, and wondering "what is this doing?",
172// you're not alone! I'll try to break this down step by step:
173//
174// # What's up with CRT$XLB?
175//
176// For anything about TLS destructors to work on Windows, we have to be able
177// to run *something* when a thread exits. To do so, we place a very special
178// static in a very special location. If this is encoded in just the right
179// way, the kernel's loader is apparently nice enough to run some function
180// of ours whenever a thread exits! How nice of the kernel!
181//
182// Lots of detailed information can be found in source [1] above, but the
183// gist of it is that this is leveraging a feature of Microsoft's PE format
184// (executable format) which is not actually used by any compilers today.
185// This apparently translates to any callbacks in the ".CRT$XLB" section
186// being run on certain events.
187//
188// So after all that, we use the compiler's #[link_section] feature to place
189// a callback pointer into the magic section so it ends up being called.
190//
191// # What's up with this callback?
192//
193// The callback specified receives a number of parameters from... someone!
85aaf69f 194// (the kernel? the runtime? I'm not quite sure!) There are a few events that
1a4d82fc
JJ
195// this gets invoked for, but we're currently only interested on when a
196// thread or a process "detaches" (exits). The process part happens for the
197// last thread and the thread part happens for any normal thread.
198//
199// # Ok, what's up with running all these destructors?
200//
201// This will likely need to be improved over time, but this function
202// attempts a "poor man's" destructor callback system. To do this we clone a
203// local copy of the dtor list to start out with. This is our fudgy attempt
204// to not hold the lock while destructors run and not worry about the list
205// changing while we're looking at it.
206//
207// Once we've got a list of what to run, we iterate over all keys, check
208// their values, and then run destructors if the values turn out to be non
209// null (setting them to null just beforehand). We do this a few times in a
210// loop to basically match Unix semantics. If we don't reach a fixed point
211// after a short while then we just inevitably leak something most likely.
212//
213// # The article mentions crazy stuff about "/INCLUDE"?
214//
e9174d1e
SL
215// It sure does! Specifically we're talking about this quote:
216//
217// The Microsoft run-time library facilitates this process by defining a
218// memory image of the TLS Directory and giving it the special name
219// “__tls_used” (Intel x86 platforms) or “_tls_used” (other platforms). The
220// linker looks for this memory image and uses the data there to create the
221// TLS Directory. Other compilers that support TLS and work with the
222// Microsoft linker must use this same technique.
223//
224// Basically what this means is that if we want support for our TLS
225// destructors/our hook being called then we need to make sure the linker does
226// not omit this symbol. Otherwise it will omit it and our callback won't be
227// wired up.
228//
229// We don't actually use the `/INCLUDE` linker flag here like the article
230// mentions because the Rust compiler doesn't propagate linker flags, but
231// instead we use a shim function which performs a volatile 1-byte load from
232// the address of the symbol to ensure it sticks around.
1a4d82fc
JJ
233
234#[link_section = ".CRT$XLB"]
235#[linkage = "external"]
236#[allow(warnings)]
92a42be0
SL
237pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD,
238 c::LPVOID) =
1a4d82fc
JJ
239 on_tls_callback;
240
241#[allow(warnings)]
92a42be0
SL
242unsafe extern "system" fn on_tls_callback(h: c::LPVOID,
243 dwReason: c::DWORD,
244 pv: c::LPVOID) {
245 if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH {
1a4d82fc
JJ
246 run_dtors();
247 }
e9174d1e
SL
248
249 // See comments above for what this is doing. Note that we don't need this
250 // trickery on GNU windows, just on MSVC.
251 reference_tls_used();
252 #[cfg(target_env = "msvc")]
253 unsafe fn reference_tls_used() {
254 extern { static _tls_used: u8; }
255 ::intrinsics::volatile_load(&_tls_used);
256 }
257 #[cfg(not(target_env = "msvc"))]
258 unsafe fn reference_tls_used() {}
1a4d82fc
JJ
259}
260
85aaf69f 261#[allow(dead_code)] // actually called above
1a4d82fc
JJ
262unsafe fn run_dtors() {
263 let mut any_run = true;
85aaf69f 264 for _ in 0..5 {
1a4d82fc
JJ
265 if !any_run { break }
266 any_run = false;
267 let dtors = {
268 DTOR_LOCK.lock();
c34b1796 269 let ret = if DTORS as usize <= 1 {
1a4d82fc
JJ
270 Vec::new()
271 } else {
272 (*DTORS).iter().map(|s| *s).collect()
273 };
274 DTOR_LOCK.unlock();
275 ret
276 };
85aaf69f 277 for &(key, dtor) in &dtors {
92a42be0 278 let ptr = c::TlsGetValue(key);
1a4d82fc 279 if !ptr.is_null() {
92a42be0 280 c::TlsSetValue(key, ptr::null_mut());
1a4d82fc
JJ
281 dtor(ptr as *mut _);
282 any_run = true;
283 }
284 }
285 }
286}