]> git.proxmox.com Git - rustc.git/blame - src/libstd/sys/windows/thread_local.rs
Imported Upstream version 1.2.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
13use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL};
14
85aaf69f 15use ptr;
1a4d82fc 16use rt;
62682a34 17use sys_common::mutex::Mutex;
1a4d82fc
JJ
18
19pub type Key = DWORD;
20pub type Dtor = unsafe extern fn(*mut u8);
21
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.
27//
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!
33//
bd371182 34// To accomplish this feat, we perform a number of threads, all contained
1a4d82fc
JJ
35// within this module:
36//
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
39// we know about.
40// * When a TLS key is destroyed, we're sure to remove it from the dtor list
41// if it's in there.
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.
44//
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.
49//
50// For more details and nitty-gritty, see the code sections below!
51//
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
55
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).
62682a34 60static DTOR_LOCK: Mutex = Mutex::new();
1a4d82fc
JJ
61static mut DTORS: *mut Vec<(Key, Dtor)> = 0 as *mut _;
62
63// -------------------------------------------------------------------------
64// Native bindings
65//
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.
68
69#[inline]
70pub unsafe fn create(dtor: Option<Dtor>) -> Key {
71 const TLS_OUT_OF_INDEXES: DWORD = 0xFFFFFFFF;
72 let key = TlsAlloc();
73 assert!(key != TLS_OUT_OF_INDEXES);
74 match dtor {
75 Some(f) => register_dtor(key, f),
76 None => {}
77 }
78 return key;
79}
80
81#[inline]
82pub unsafe fn set(key: Key, value: *mut u8) {
83 let r = TlsSetValue(key, value as LPVOID);
84 debug_assert!(r != 0);
85}
86
87#[inline]
88pub unsafe fn get(key: Key) -> *mut u8 {
89 TlsGetValue(key) as *mut u8
90}
91
92#[inline]
93pub 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.
99 //
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
104 // thing to know!
105 //
106 // For now we just leak all keys with dtors to "fix" this.
107 // Note that source [2] above shows precedent for this sort
108 // of strategy.
109 } else {
110 let r = TlsFree(key);
111 debug_assert!(r != 0);
112 }
113}
114
115extern "system" {
116 fn TlsAlloc() -> DWORD;
117 fn TlsFree(dwTlsIndex: DWORD) -> BOOL;
118 fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID;
119 fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL;
120}
121
122// -------------------------------------------------------------------------
123// Dtor registration
124//
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.
128//
129// FIXME: This could probably be at least a little faster with a BTree.
130
131unsafe fn init_dtors() {
132 if !DTORS.is_null() { return }
133
134 let dtors = box Vec::<(Key, Dtor)>::new();
1a4d82fc 135
c34b1796 136 let res = rt::at_exit(move|| {
1a4d82fc
JJ
137 DTOR_LOCK.lock();
138 let dtors = DTORS;
c34b1796
AL
139 DTORS = 1 as *mut _;
140 Box::from_raw(dtors);
141 assert!(DTORS as usize == 1); // can't re-init after destructing
1a4d82fc
JJ
142 DTOR_LOCK.unlock();
143 });
c34b1796 144 if res.is_ok() {
62682a34 145 DTORS = Box::into_raw(dtors);
c34b1796
AL
146 } else {
147 DTORS = 1 as *mut _;
148 }
1a4d82fc
JJ
149}
150
151unsafe fn register_dtor(key: Key, dtor: Dtor) {
152 DTOR_LOCK.lock();
153 init_dtors();
c34b1796
AL
154 assert!(DTORS as usize != 0);
155 assert!(DTORS as usize != 1,
156 "cannot create new TLS keys after the main thread has exited");
1a4d82fc
JJ
157 (*DTORS).push((key, dtor));
158 DTOR_LOCK.unlock();
159}
160
161unsafe fn unregister_dtor(key: Key) -> bool {
162 DTOR_LOCK.lock();
163 init_dtors();
c34b1796
AL
164 assert!(DTORS as usize != 0);
165 assert!(DTORS as usize != 1,
166 "cannot unregister destructors after the main thread has exited");
1a4d82fc
JJ
167 let ret = {
168 let dtors = &mut *DTORS;
169 let before = dtors.len();
170 dtors.retain(|&(k, _)| k != key);
171 dtors.len() != before
172 };
173 DTOR_LOCK.unlock();
174 ret
175}
176
177// -------------------------------------------------------------------------
178// Where the Magic (TM) Happens
179//
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:
182//
183// # What's up with CRT$XLB?
184//
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!
190//
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.
196//
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.
199//
200// # What's up with this callback?
201//
202// The callback specified receives a number of parameters from... someone!
85aaf69f 203// (the kernel? the runtime? I'm not quite sure!) There are a few events that
1a4d82fc
JJ
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.
207//
208// # Ok, what's up with running all these destructors?
209//
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.
215//
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.
221//
222// # The article mentions crazy stuff about "/INCLUDE"?
223//
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?
226
227#[link_section = ".CRT$XLB"]
228#[linkage = "external"]
229#[allow(warnings)]
230pub static p_thread_callback: unsafe extern "system" fn(LPVOID, DWORD,
231 LPVOID) =
232 on_tls_callback;
233
234#[allow(warnings)]
235unsafe extern "system" fn on_tls_callback(h: LPVOID,
236 dwReason: DWORD,
237 pv: 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 {
241 run_dtors();
242 }
243}
244
85aaf69f 245#[allow(dead_code)] // actually called above
1a4d82fc
JJ
246unsafe fn run_dtors() {
247 let mut any_run = true;
85aaf69f 248 for _ in 0..5 {
1a4d82fc
JJ
249 if !any_run { break }
250 any_run = false;
251 let dtors = {
252 DTOR_LOCK.lock();
c34b1796 253 let ret = if DTORS as usize <= 1 {
1a4d82fc
JJ
254 Vec::new()
255 } else {
256 (*DTORS).iter().map(|s| *s).collect()
257 };
258 DTOR_LOCK.unlock();
259 ret
260 };
85aaf69f 261 for &(key, dtor) in &dtors {
1a4d82fc
JJ
262 let ptr = TlsGetValue(key);
263 if !ptr.is_null() {
85aaf69f 264 TlsSetValue(key, ptr::null_mut());
1a4d82fc
JJ
265 dtor(ptr as *mut _);
266 any_run = true;
267 }
268 }
269 }
270}