]> git.lizzy.rs Git - rust.git/blob - src/libstd/rt/local_ptr.rs
auto merge of #13967 : richo/rust/features/ICE-fails, r=alexcrichton
[rust.git] / src / libstd / rt / local_ptr.rs
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 }