(index<- ) ./libstd/rt/local_ptr.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Fri May 9 13:02:28 2014
1 // Copyright 2013 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 //! Access to a single thread-local pointer.
12 //!
13 //! The runtime will use this for storing Box<Task>.
14 //!
15 //! FIXME: Add runtime checks for usage of inconsistent pointer types.
16 //! and for overwriting an existing pointer.
17
18 #![allow(dead_code)]
19
20 use cast;
21 use ops::{Drop, Deref, DerefMut};
22 use owned::Box;
23 use ptr::RawPtr;
24
25 #[cfg(windows)] // mingw-w32 doesn't like thread_local things
26 #[cfg(target_os = "android")] // see #10686
27 pub use self::native::{init, cleanup, put, take, try_take, unsafe_take, exists,
28 unsafe_borrow, try_unsafe_borrow};
29
30 #[cfg(not(windows), not(target_os = "android"))]
31 pub use self::compiled::{init, cleanup, put, take, try_take, unsafe_take, exists,
32 unsafe_borrow, try_unsafe_borrow};
33
34 /// Encapsulates a borrowed value. When this value goes out of scope, the
35 /// pointer is returned.
36 pub struct Borrowed<T> {
37 val: *(),
38 }
39
40 #[unsafe_destructor]
41 impl<T> Drop for Borrowed<T> {
42 fn drop(&mut self) {
43 unsafe {
44 if self.val.is_null() {
45 rtabort!("Aiee, returning null borrowed object!");
46 }
47 let val: Box<T> = cast::transmute(self.val);
48 put::<T>(val);
49 rtassert!(exists());
50 }
51 }
52 }
53
54 impl<T> Deref<T> for Borrowed<T> {
55 fn deref<'a>(&'a self) -> &'a T {
56 unsafe { &*(self.val as *T) }
57 }
58 }
59
60 impl<T> DerefMut<T> for Borrowed<T> {
61 fn deref_mut<'a>(&'a mut self) -> &'a mut T {
62 unsafe { &mut *(self.val as *mut T) }
63 }
64 }
65
66 /// Borrow the thread-local value from thread-local storage.
67 /// While the value is borrowed it is not available in TLS.
68 ///
69 /// # Safety note
70 ///
71 /// Does not validate the pointer type.
72 #[inline]
73 pub unsafe fn borrow<T>() -> Borrowed<T> {
74 let val: *() = cast::transmute(take::<T>());
75 Borrowed {
76 val: val,
77 }
78 }
79
80 /// Compiled implementation of accessing the runtime local pointer. This is
81 /// implemented using LLVM's thread_local attribute which isn't necessarily
82 /// working on all platforms. This implementation is faster, however, so we use
83 /// it wherever possible.
84 #[cfg(not(windows), not(target_os = "android"))]
85 pub mod compiled {
86 use cast;
87 use option::{Option, Some, None};
88 use owned::Box;
89 use ptr::RawPtr;
90
91 #[cfg(test)]
92 pub use realstd::rt::shouldnt_be_public::RT_TLS_PTR;
93
94 #[cfg(not(test))]
95 #[thread_local]
96 pub static mut RT_TLS_PTR: *mut u8 = 0 as *mut u8;
97
98 pub fn init() {}
99
100 pub unsafe fn cleanup() {}
101
102 // Rationale for all of these functions being inline(never)
103 //
104 // The #[thread_local] annotation gets propagated all the way through to
105 // LLVM, meaning the global is specially treated by LLVM to lower it to an
106 // efficient sequence of instructions. This also involves dealing with fun
107 // stuff in object files and whatnot. Regardless, it turns out this causes
108 // trouble with green threads and lots of optimizations turned on. The
109 // following case study was done on linux x86_64, but I would imagine that
110 // other platforms are similar.
111 //
112 // On linux, the instruction sequence for loading the tls pointer global
113 // looks like:
114 //
115 // mov %fs:0x0, %rax
116 // mov -0x8(%rax), %rbx
117 //
118 // This code leads me to believe that (%fs:0x0) is a table, and then the
119 // table contains the TLS values for the process. Hence, the slot at offset
120 // -0x8 is the task TLS pointer. This leads us to the conclusion that this
121 // table is the actual thread local part of each thread. The kernel sets up
122 // the fs segment selector to point at the right region of memory for each
123 // thread.
124 //
125 // Optimizations lead me to believe that this code is lowered to these
126 // instructions in the LLVM codegen passes, because you'll see code like
127 // this when everything is optimized:
128 //
129 // mov %fs:0x0, %r14
130 // mov -0x8(%r14), %rbx
131 // // do something with %rbx, the rust Task pointer
132 //
133 // ... // <- do more things
134 //
135 // mov -0x8(%r14), %rbx
136 // // do something else with %rbx
137 //
138 // Note that the optimization done here is that the first load is not
139 // duplicated during the lower instructions. This means that the %fs:0x0
140 // memory location is only dereferenced once.
141 //
142 // Normally, this is actually a good thing! With green threads, however,
143 // it's very possible for the code labeled "do more things" to context
144 // switch to another thread. If this happens, then we *must* re-load %fs:0x0
145 // because it's changed (we're on a different thread). If we don't re-load
146 // the table location, then we'll be reading the original thread's TLS
147 // values, not our thread's TLS values.
148 //
149 // Hence, we never inline these functions. By never inlining, we're
150 // guaranteed that loading the table is a local decision which is forced to
151 // *always* happen.
152
153 /// Give a pointer to thread-local storage.
154 ///
155 /// # Safety note
156 ///
157 /// Does not validate the pointer type.
158 #[inline(never)] // see comments above
159 pub unsafe fn put<T>(sched: Box<T>) {
160 RT_TLS_PTR = cast::transmute(sched)
161 }
162
163 /// Take ownership of a pointer from thread-local storage.
164 ///
165 /// # Safety note
166 ///
167 /// Does not validate the pointer type.
168 #[inline(never)] // see comments above
169 pub unsafe fn take<T>() -> Box<T> {
170 let ptr = RT_TLS_PTR;
171 rtassert!(!ptr.is_null());
172 let ptr: Box<T> = cast::transmute(ptr);
173 // can't use `as`, due to type not matching with `cfg(test)`
174 RT_TLS_PTR = cast::transmute(0);
175 ptr
176 }
177
178 /// Optionally take ownership of a pointer from thread-local storage.
179 ///
180 /// # Safety note
181 ///
182 /// Does not validate the pointer type.
183 #[inline(never)] // see comments above
184 pub unsafe fn try_take<T>() -> Option<Box<T>> {
185 let ptr = RT_TLS_PTR;
186 if ptr.is_null() {
187 None
188 } else {
189 let ptr: Box<T> = cast::transmute(ptr);
190 // can't use `as`, due to type not matching with `cfg(test)`
191 RT_TLS_PTR = cast::transmute(0);
192 Some(ptr)
193 }
194 }
195
196 /// Take ownership of a pointer from thread-local storage.
197 ///
198 /// # Safety note
199 ///
200 /// Does not validate the pointer type.
201 /// Leaves the old pointer in TLS for speed.
202 #[inline(never)] // see comments above
203 pub unsafe fn unsafe_take<T>() -> Box<T> {
204 cast::transmute(RT_TLS_PTR)
205 }
206
207 /// Check whether there is a thread-local pointer installed.
208 #[inline(never)] // see comments above
209 pub fn exists() -> bool {
210 unsafe {
211 RT_TLS_PTR.is_not_null()
212 }
213 }
214
215 #[inline(never)] // see comments above
216 pub unsafe fn unsafe_borrow<T>() -> *mut T {
217 if RT_TLS_PTR.is_null() {
218 rtabort!("thread-local pointer is null. bogus!");
219 }
220 RT_TLS_PTR as *mut T
221 }
222
223 #[inline(never)] // see comments above
224 pub unsafe fn try_unsafe_borrow<T>() -> Option<*mut T> {
225 if RT_TLS_PTR.is_null() {
226 None
227 } else {
228 Some(RT_TLS_PTR as *mut T)
229 }
230 }
231 }
232
233 /// Native implementation of having the runtime thread-local pointer. This
234 /// implementation uses the `thread_local_storage` module to provide a
235 /// thread-local value.
236 pub mod native {
237 use cast;
238 use option::{Option, Some, None};
239 use owned::Box;
240 use ptr;
241 use ptr::RawPtr;
242 use tls = rt::thread_local_storage;
243
244 static mut RT_TLS_KEY: tls::Key = -1;
245
246 /// Initialize the TLS key. Other ops will fail if this isn't executed
247 /// first.
248 pub fn init() {
249 unsafe {
250 tls::create(&mut RT_TLS_KEY);
251 }
252 }
253
254 pub unsafe fn cleanup() {
255 rtassert!(RT_TLS_KEY != -1);
256 tls::destroy(RT_TLS_KEY);
257 }
258
259 /// Give a pointer to thread-local storage.
260 ///
261 /// # Safety note
262 ///
263 /// Does not validate the pointer type.
264 #[inline]
265 pub unsafe fn put<T>(sched: Box<T>) {
266 let key = tls_key();
267 let void_ptr: *mut u8 = cast::transmute(sched);
268 tls::set(key, void_ptr);
269 }
270
271 /// Take ownership of a pointer from thread-local storage.
272 ///
273 /// # Safety note
274 ///
275 /// Does not validate the pointer type.
276 #[inline]
277 pub unsafe fn take<T>() -> Box<T> {
278 let key = tls_key();
279 let void_ptr: *mut u8 = tls::get(key);
280 if void_ptr.is_null() {
281 rtabort!("thread-local pointer is null. bogus!");
282 }
283 let ptr: Box<T> = cast::transmute(void_ptr);
284 tls::set(key, ptr::mut_null());
285 return ptr;
286 }
287
288 /// Optionally take ownership of a pointer from thread-local storage.
289 ///
290 /// # Safety note
291 ///
292 /// Does not validate the pointer type.
293 #[inline]
294 pub unsafe fn try_take<T>() -> Option<Box<T>> {
295 match maybe_tls_key() {
296 Some(key) => {
297 let void_ptr: *mut u8 = tls::get(key);
298 if void_ptr.is_null() {
299 None
300 } else {
301 let ptr: Box<T> = cast::transmute(void_ptr);
302 tls::set(key, ptr::mut_null());
303 Some(ptr)
304 }
305 }
306 None => None
307 }
308 }
309
310 /// Take ownership of a pointer from thread-local storage.
311 ///
312 /// # Safety note
313 ///
314 /// Does not validate the pointer type.
315 /// Leaves the old pointer in TLS for speed.
316 #[inline]
317 pub unsafe fn unsafe_take<T>() -> Box<T> {
318 let key = tls_key();
319 let void_ptr: *mut u8 = tls::get(key);
320 if void_ptr.is_null() {
321 rtabort!("thread-local pointer is null. bogus!");
322 }
323 let ptr: Box<T> = cast::transmute(void_ptr);
324 return ptr;
325 }
326
327 /// Check whether there is a thread-local pointer installed.
328 pub fn exists() -> bool {
329 unsafe {
330 match maybe_tls_key() {
331 Some(key) => tls::get(key).is_not_null(),
332 None => false
333 }
334 }
335 }
336
337 /// Borrow a mutable reference to the thread-local value
338 ///
339 /// # Safety Note
340 ///
341 /// Because this leaves the value in thread-local storage it is possible
342 /// For the Scheduler pointer to be aliased
343 pub unsafe fn unsafe_borrow<T>() -> *mut T {
344 let key = tls_key();
345 let void_ptr = tls::get(key);
346 if void_ptr.is_null() {
347 rtabort!("thread-local pointer is null. bogus!");
348 }
349 void_ptr as *mut T
350 }
351
352 pub unsafe fn try_unsafe_borrow<T>() -> Option<*mut T> {
353 match maybe_tls_key() {
354 Some(key) => {
355 let void_ptr = tls::get(key);
356 if void_ptr.is_null() {
357 None
358 } else {
359 Some(void_ptr as *mut T)
360 }
361 }
362 None => None
363 }
364 }
365
366 #[inline]
367 fn tls_key() -> tls::Key {
368 match maybe_tls_key() {
369 Some(key) => key,
370 None => rtabort!("runtime tls key not initialized")
371 }
372 }
373
374 #[inline]
375 #[cfg(not(test))]
376 #[allow(visible_private_types)]
377 pub fn maybe_tls_key() -> Option<tls::Key> {
378 unsafe {
379 // NB: This is a little racy because, while the key is
380 // initialized under a mutex and it's assumed to be initialized
381 // in the Scheduler ctor by any thread that needs to use it,
382 // we are not accessing the key under a mutex. Threads that
383 // are not using the new Scheduler but still *want to check*
384 // whether they are running under a new Scheduler may see a 0
385 // value here that is in the process of being initialized in
386 // another thread. I think this is fine since the only action
387 // they could take if it was initialized would be to check the
388 // thread-local value and see that it's not set.
389 if RT_TLS_KEY != -1 {
390 return Some(RT_TLS_KEY);
391 } else {
392 return None;
393 }
394 }
395 }
396
397 #[inline] #[cfg(test)]
398 pub fn maybe_tls_key() -> Option<tls::Key> {
399 use realstd;
400 unsafe {
401 cast::transmute(realstd::rt::shouldnt_be_public::maybe_tls_key())
402 }
403 }
404 }