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.
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.
11 //! Access to a single thread-local pointer.
13 //! The runtime will use this for storing Box<Task>.
15 //! FIXME: Add runtime checks for usage of inconsistent pointer types.
16 //! and for overwriting an existing pointer.
23 use alloc::boxed::Box;
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};
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};
35 /// Encapsulates a borrowed value. When this value goes out of scope, the
36 /// pointer is returned.
37 pub struct Borrowed<T> {
42 impl<T> Drop for Borrowed<T> {
45 if self.val.is_null() {
46 rtabort!("Aiee, returning null borrowed object!");
48 let val: Box<T> = mem::transmute(self.val);
55 impl<T> Deref<T> for Borrowed<T> {
56 fn deref<'a>(&'a self) -> &'a T {
57 unsafe { &*(self.val as *const T) }
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) }
67 /// Borrow the thread-local value from thread-local storage.
68 /// While the value is borrowed it is not available in TLS.
72 /// Does not validate the pointer type.
74 pub unsafe fn borrow<T>() -> Borrowed<T> {
75 let val: *const () = mem::transmute(take::<T>());
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"))]
89 use alloc::boxed::Box;
93 pub use realrustrt::shouldnt_be_public::RT_TLS_PTR;
97 pub static mut RT_TLS_PTR: *mut u8 = 0 as *mut u8;
101 pub unsafe fn cleanup() {}
103 // Rationale for all of these functions being inline(never)
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.
113 // On Linux, the instruction sequence for loading the tls pointer global
117 // mov -0x8(%rax), %rbx
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
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:
131 // mov -0x8(%r14), %rbx
132 // // do something with %rbx, the rust Task pointer
134 // ... // <- do more things
136 // mov -0x8(%r14), %rbx
137 // // do something else with %rbx
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.
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.
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
154 /// Give a pointer to thread-local storage.
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)
164 /// Take ownership of a pointer from thread-local storage.
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);
179 /// Optionally take ownership of a pointer from thread-local storage.
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;
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);
197 /// Take ownership of a pointer from thread-local storage.
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)
208 /// Check whether there is a thread-local pointer installed.
209 #[inline(never)] // see comments above
210 pub fn exists() -> bool {
212 RT_TLS_PTR.is_not_null()
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!");
224 #[inline(never)] // see comments above
225 pub unsafe fn try_unsafe_borrow<T>() -> Option<*mut T> {
226 if RT_TLS_PTR.is_null() {
229 Some(RT_TLS_PTR as *mut T)
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.
238 use core::prelude::*;
240 use alloc::boxed::Box;
243 use thread_local_storage as tls;
245 static mut RT_TLS_KEY: tls::Key = -1;
247 /// Initialize the TLS key. Other ops will fail if this isn't executed
251 tls::create(&mut RT_TLS_KEY);
255 pub unsafe fn cleanup() {
256 rtassert!(RT_TLS_KEY != -1);
257 tls::destroy(RT_TLS_KEY);
260 /// Give a pointer to thread-local storage.
264 /// Does not validate the pointer type.
266 pub unsafe fn put<T>(sched: Box<T>) {
268 let void_ptr: *mut u8 = mem::transmute(sched);
269 tls::set(key, void_ptr);
272 /// Take ownership of a pointer from thread-local storage.
276 /// Does not validate the pointer type.
278 pub unsafe fn take<T>() -> Box<T> {
280 let void_ptr: *mut u8 = tls::get(key);
281 if void_ptr.is_null() {
282 rtabort!("thread-local pointer is null. bogus!");
284 let ptr: Box<T> = mem::transmute(void_ptr);
285 tls::set(key, ptr::null_mut());
289 /// Optionally take ownership of a pointer from thread-local storage.
293 /// Does not validate the pointer type.
295 pub unsafe fn try_take<T>() -> Option<Box<T>> {
296 match maybe_tls_key() {
298 let void_ptr: *mut u8 = tls::get(key);
299 if void_ptr.is_null() {
302 let ptr: Box<T> = mem::transmute(void_ptr);
303 tls::set(key, ptr::null_mut());
311 /// Take ownership of a pointer from thread-local storage.
315 /// Does not validate the pointer type.
316 /// Leaves the old pointer in TLS for speed.
318 pub unsafe fn unsafe_take<T>() -> Box<T> {
320 let void_ptr: *mut u8 = tls::get(key);
321 if void_ptr.is_null() {
322 rtabort!("thread-local pointer is null. bogus!");
324 let ptr: Box<T> = mem::transmute(void_ptr);
328 /// Check whether there is a thread-local pointer installed.
329 pub fn exists() -> bool {
331 match maybe_tls_key() {
332 Some(key) => tls::get(key).is_not_null(),
338 /// Borrow a mutable reference to the thread-local value
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 {
346 let void_ptr = tls::get(key);
347 if void_ptr.is_null() {
348 rtabort!("thread-local pointer is null. bogus!");
353 pub unsafe fn try_unsafe_borrow<T>() -> Option<*mut T> {
354 match maybe_tls_key() {
356 let void_ptr = tls::get(key);
357 if void_ptr.is_null() {
360 Some(void_ptr as *mut T)
368 fn tls_key() -> tls::Key {
369 match maybe_tls_key() {
371 None => rtabort!("runtime tls key not initialized")
377 pub fn maybe_tls_key() -> Option<tls::Key> {
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);
397 #[inline] #[cfg(test)]
398 pub fn maybe_tls_key() -> Option<tls::Key> {
401 mem::transmute(realrustrt::shouldnt_be_public::maybe_tls_key())