]>
Commit | Line | Data |
---|---|---|
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 | ||
11 | use prelude::v1::*; | |
12 | ||
13 | use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL}; | |
14 | ||
85aaf69f | 15 | use ptr; |
1a4d82fc | 16 | use rt; |
62682a34 | 17 | use sys_common::mutex::Mutex; |
1a4d82fc JJ |
18 | |
19 | pub type Key = DWORD; | |
20 | pub 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 | 60 | static DTOR_LOCK: Mutex = Mutex::new(); |
1a4d82fc JJ |
61 | static 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] | |
70 | pub 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] | |
82 | pub 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] | |
88 | pub unsafe fn get(key: Key) -> *mut u8 { | |
89 | TlsGetValue(key) as *mut u8 | |
90 | } | |
91 | ||
92 | #[inline] | |
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. | |
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 | ||
115 | extern "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 | ||
131 | unsafe 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 | ||
151 | unsafe 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 | ||
161 | unsafe 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)] | |
230 | pub static p_thread_callback: unsafe extern "system" fn(LPVOID, DWORD, | |
231 | LPVOID) = | |
232 | on_tls_callback; | |
233 | ||
234 | #[allow(warnings)] | |
235 | unsafe 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 |
246 | unsafe 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 | } |