]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/windows/thread_local.rs
auto merge of #21132 : sfackler/rust/wait_timeout, r=alexcrichton
[rust.git] / 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.
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
15 use mem;
16 use rt;
17 use sys_common::mutex::{MUTEX_INIT, Mutex};
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 //
34 // To accomplish this feat, we perform a number of tasks, all contained
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).
60 static DTOR_LOCK: Mutex = MUTEX_INIT;
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();
135     DTORS = mem::transmute(dtors);
136
137     rt::at_exit(move|| {
138         DTOR_LOCK.lock();
139         let dtors = DTORS;
140         DTORS = 0 as *mut _;
141         mem::transmute::<_, Box<Vec<(Key, Dtor)>>>(dtors);
142         assert!(DTORS.is_null()); // can't re-init after destructing
143         DTOR_LOCK.unlock();
144     });
145 }
146
147 unsafe fn register_dtor(key: Key, dtor: Dtor) {
148     DTOR_LOCK.lock();
149     init_dtors();
150     (*DTORS).push((key, dtor));
151     DTOR_LOCK.unlock();
152 }
153
154 unsafe fn unregister_dtor(key: Key) -> bool {
155     DTOR_LOCK.lock();
156     init_dtors();
157     let ret = {
158         let dtors = &mut *DTORS;
159         let before = dtors.len();
160         dtors.retain(|&(k, _)| k != key);
161         dtors.len() != before
162     };
163     DTOR_LOCK.unlock();
164     ret
165 }
166
167 // -------------------------------------------------------------------------
168 // Where the Magic (TM) Happens
169 //
170 // If you're looking at this code, and wondering "what is this doing?",
171 // you're not alone! I'll try to break this down step by step:
172 //
173 // # What's up with CRT$XLB?
174 //
175 // For anything about TLS destructors to work on Windows, we have to be able
176 // to run *something* when a thread exits. To do so, we place a very special
177 // static in a very special location. If this is encoded in just the right
178 // way, the kernel's loader is apparently nice enough to run some function
179 // of ours whenever a thread exits! How nice of the kernel!
180 //
181 // Lots of detailed information can be found in source [1] above, but the
182 // gist of it is that this is leveraging a feature of Microsoft's PE format
183 // (executable format) which is not actually used by any compilers today.
184 // This apparently translates to any callbacks in the ".CRT$XLB" section
185 // being run on certain events.
186 //
187 // So after all that, we use the compiler's #[link_section] feature to place
188 // a callback pointer into the magic section so it ends up being called.
189 //
190 // # What's up with this callback?
191 //
192 // The callback specified receives a number of parameters from... someone!
193 // (the kernel? the runtime? I'm not qute sure!) There are a few events that
194 // this gets invoked for, but we're currently only interested on when a
195 // thread or a process "detaches" (exits). The process part happens for the
196 // last thread and the thread part happens for any normal thread.
197 //
198 // # Ok, what's up with running all these destructors?
199 //
200 // This will likely need to be improved over time, but this function
201 // attempts a "poor man's" destructor callback system. To do this we clone a
202 // local copy of the dtor list to start out with. This is our fudgy attempt
203 // to not hold the lock while destructors run and not worry about the list
204 // changing while we're looking at it.
205 //
206 // Once we've got a list of what to run, we iterate over all keys, check
207 // their values, and then run destructors if the values turn out to be non
208 // null (setting them to null just beforehand). We do this a few times in a
209 // loop to basically match Unix semantics. If we don't reach a fixed point
210 // after a short while then we just inevitably leak something most likely.
211 //
212 // # The article mentions crazy stuff about "/INCLUDE"?
213 //
214 // It sure does! This seems to work for now, so maybe we'll just run into
215 // that if we start linking with msvc?
216
217 #[link_section = ".CRT$XLB"]
218 #[linkage = "external"]
219 #[allow(warnings)]
220 pub static p_thread_callback: unsafe extern "system" fn(LPVOID, DWORD,
221                                                         LPVOID) =
222         on_tls_callback;
223
224 #[allow(warnings)]
225 unsafe extern "system" fn on_tls_callback(h: LPVOID,
226                                           dwReason: DWORD,
227                                           pv: LPVOID) {
228     const DLL_THREAD_DETACH: DWORD = 3;
229     const DLL_PROCESS_DETACH: DWORD = 0;
230     if dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH {
231         run_dtors();
232     }
233 }
234
235 unsafe fn run_dtors() {
236     let mut any_run = true;
237     for _ in range(0, 5i) {
238         if !any_run { break }
239         any_run = false;
240         let dtors = {
241             DTOR_LOCK.lock();
242             let ret = if DTORS.is_null() {
243                 Vec::new()
244             } else {
245                 (*DTORS).iter().map(|s| *s).collect()
246             };
247             DTOR_LOCK.unlock();
248             ret
249         };
250         for &(key, dtor) in dtors.iter() {
251             let ptr = TlsGetValue(key);
252             if !ptr.is_null() {
253                 TlsSetValue(key, 0 as *mut _);
254                 dtor(ptr as *mut _);
255                 any_run = true;
256             }
257         }
258     }
259 }