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