]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/windows/thread_local.rs
std: Add a new top-level thread_local module
[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::*;
12
13 use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL};
14
15 use mem;
16 use rustrt;
17 use rustrt::exclusive::Exclusive;
18 use sync::{ONCE_INIT, Once};
19
20 pub type Key = DWORD;
21 pub type Dtor = unsafe extern fn(*mut u8);
22
23 // Turns out, like pretty much everything, Windows is pretty close the
24 // functionality that Unix provides, but slightly different! In the case of
25 // TLS, Windows does not provide an API to provide a destructor for a TLS
26 // variable. This ends up being pretty crucial to this implementation, so we
27 // need a way around this.
28 //
29 // The solution here ended up being a little obscure, but fear not, the
30 // internet has informed me [1][2] that this solution is not unique (no way
31 // I could have thought of it as well!). The key idea is to insert some hook
32 // somewhere to run arbitrary code on thread termination. With this in place
33 // we'll be able to run anything we like, including all TLS destructors!
34 //
35 // To accomplish this feat, we perform a number of tasks, all contained
36 // within this module:
37 //
38 // * All TLS destructors are tracked by *us*, not the windows runtime. This
39 //   means that we have a global list of destructors for each TLS key that
40 //   we know about.
41 // * When a TLS key is destroyed, we're sure to remove it from the dtor list
42 //   if it's in there.
43 // * When a thread exits, we run over the entire list and run dtors for all
44 //   non-null keys. This attempts to match Unix semantics in this regard.
45 //
46 // This ends up having the overhead of using a global list, having some
47 // locks here and there, and in general just adding some more code bloat. We
48 // attempt to optimize runtime by forgetting keys that don't have
49 // destructors, but this only gets us so far.
50 //
51 // For more details and nitty-gritty, see the code sections below!
52 //
53 // [1]: http://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way
54 // [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base
55 //                        /threading/thread_local_storage_win.cc#L42
56
57 static INIT_DTORS: Once = ONCE_INIT;
58 static mut DTORS: *mut Exclusive<Vec<(Key, Dtor)>> = 0 as *mut _;
59
60 // -------------------------------------------------------------------------
61 // Native bindings
62 //
63 // This section is just raw bindings to the native functions that Windows
64 // provides, There's a few extra calls to deal with destructors.
65
66 #[inline]
67 pub unsafe fn create(dtor: Option<Dtor>) -> Key {
68     const TLS_OUT_OF_INDEXES: DWORD = 0xFFFFFFFF;
69     let key = TlsAlloc();
70     assert!(key != TLS_OUT_OF_INDEXES);
71     match dtor {
72         Some(f) => register_dtor(key, f),
73         None => {}
74     }
75     return key;
76 }
77
78 #[inline]
79 pub unsafe fn set(key: Key, value: *mut u8) {
80     let r = TlsSetValue(key, value as LPVOID);
81     debug_assert!(r != 0);
82 }
83
84 #[inline]
85 pub unsafe fn get(key: Key) -> *mut u8 {
86     TlsGetValue(key) as *mut u8
87 }
88
89 #[inline]
90 pub unsafe fn destroy(key: Key) {
91     if unregister_dtor(key) {
92         // FIXME: Currently if a key has a destructor associated with it we
93         // can't actually ever unregister it. If we were to
94         // unregister it, then any key destruction would have to be
95         // serialized with respect to actually running destructors.
96         //
97         // We want to avoid a race where right before run_dtors runs
98         // some destructors TlsFree is called. Allowing the call to
99         // TlsFree would imply that the caller understands that *all
100         // known threads* are not exiting, which is quite a difficult
101         // thing to know!
102         //
103         // For now we just leak all keys with dtors to "fix" this.
104         // Note that source [2] above shows precedent for this sort
105         // of strategy.
106     } else {
107         let r = TlsFree(key);
108         debug_assert!(r != 0);
109     }
110 }
111
112 extern "system" {
113     fn TlsAlloc() -> DWORD;
114     fn TlsFree(dwTlsIndex: DWORD) -> BOOL;
115     fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID;
116     fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL;
117 }
118
119 // -------------------------------------------------------------------------
120 // Dtor registration
121 //
122 // These functions are associated with registering and unregistering
123 // destructors. They're pretty simple, they just push onto a vector and scan
124 // a vector currently.
125 //
126 // FIXME: This could probably be at least a little faster with a BTree.
127
128 fn init_dtors() {
129     let dtors = box Exclusive::new(Vec::<(Key, Dtor)>::new());
130     unsafe {
131         DTORS = mem::transmute(dtors);
132     }
133
134     rustrt::at_exit(proc() unsafe {
135         mem::transmute::<_, Box<Exclusive<Vec<(Key, Dtor)>>>>(DTORS);
136         DTORS = 0 as *mut _;
137     });
138 }
139
140 unsafe fn register_dtor(key: Key, dtor: Dtor) {
141     INIT_DTORS.doit(init_dtors);
142     let mut dtors = (*DTORS).lock();
143     dtors.push((key, dtor));
144 }
145
146 unsafe fn unregister_dtor(key: Key) -> bool {
147     if DTORS.is_null() { return false }
148     let mut dtors = (*DTORS).lock();
149     let before = dtors.len();
150     dtors.retain(|&(k, _)| k != key);
151     dtors.len() != before
152 }
153
154 // -------------------------------------------------------------------------
155 // Where the Magic (TM) Happens
156 //
157 // If you're looking at this code, and wondering "what is this doing?",
158 // you're not alone! I'll try to break this down step by step:
159 //
160 // # What's up with CRT$XLB?
161 //
162 // For anything about TLS destructors to work on Windows, we have to be able
163 // to run *something* when a thread exits. To do so, we place a very special
164 // static in a very special location. If this is encoded in just the right
165 // way, the kernel's loader is apparently nice enough to run some function
166 // of ours whenever a thread exits! How nice of the kernel!
167 //
168 // Lots of detailed information can be found in source [1] above, but the
169 // gist of it is that this is leveraging a feature of Microsoft's PE format
170 // (executable format) which is not actually used by any compilers today.
171 // This apparently translates to any callbacks in the ".CRT$XLB" section
172 // being run on certain events.
173 //
174 // So after all that, we use the compiler's #[link_section] feature to place
175 // a callback pointer into the magic section so it ends up being called.
176 //
177 // # What's up with this callback?
178 //
179 // The callback specified receives a number of parameters from... someone!
180 // (the kernel? the runtime? I'm not qute sure!) There are a few events that
181 // this gets invoked for, but we're currentl only interested on when a
182 // thread or a process "detaches" (exits). The process part happens for the
183 // last thread and the thread part happens for any normal thread.
184 //
185 // # Ok, what's up with running all these destructors?
186 //
187 // This will likely need to be improved over time, but this function
188 // attempts a "poor man's" destructor callback system. To do this we clone a
189 // local copy of the dtor list to start out with. This is our fudgy attempt
190 // to not hold the lock while destructors run and not worry about the list
191 // changing while we're looking at it.
192 //
193 // Once we've got a list of what to run, we iterate over all keys, check
194 // their values, and then run destructors if the values turn out to be non
195 // null (setting them to null just beforehand). We do this a few times in a
196 // loop to basically match Unix semantics. If we don't reach a fixed point
197 // after a short while then we just inevitably leak something most likely.
198 //
199 // # The article mentions crazy stuff about "/INCLUDE"?
200 //
201 // It sure does! This seems to work for now, so maybe we'll just run into
202 // that if we start linking with msvc?
203
204 #[link_section = ".CRT$XLB"]
205 #[linkage = "external"]
206 #[allow(warnings)]
207 pub static p_thread_callback: unsafe extern "system" fn(LPVOID, DWORD,
208                                                         LPVOID) =
209         on_tls_callback;
210
211 #[allow(warnings)]
212 unsafe extern "system" fn on_tls_callback(h: LPVOID,
213                                           dwReason: DWORD,
214                                           pv: LPVOID) {
215     const DLL_THREAD_DETACH: DWORD = 3;
216     const DLL_PROCESS_DETACH: DWORD = 0;
217     if dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH {
218         run_dtors();
219     }
220 }
221
222 unsafe fn run_dtors() {
223     if DTORS.is_null() { return }
224     let mut any_run = true;
225     for _ in range(0, 5i) {
226         if !any_run { break }
227         any_run = false;
228         let dtors = (*DTORS).lock().iter().map(|p| *p).collect::<Vec<_>>();
229         for &(key, dtor) in dtors.iter() {
230             let ptr = TlsGetValue(key);
231             if !ptr.is_null() {
232                 TlsSetValue(key, 0 as *mut _);
233                 dtor(ptr as *mut _);
234                 any_run = true;
235             }
236         }
237     }
238 }