// Copyright 2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Access to a single thread-local pointer. //! //! The runtime will use this for storing Box. //! //! FIXME: Add runtime checks for usage of inconsistent pointer types. //! and for overwriting an existing pointer. #![allow(dead_code)] use core::prelude::*; use core::mem; use alloc::boxed::Box; #[cfg(windows)] // mingw-w32 doesn't like thread_local things #[cfg(target_os = "android")] // see #10686 #[cfg(target_os = "ios")] pub use self::native::{init, cleanup, put, take, try_take, unsafe_take, exists, unsafe_borrow, try_unsafe_borrow}; #[cfg(not(windows), not(target_os = "android"), not(target_os = "ios"))] pub use self::compiled::{init, cleanup, put, take, try_take, unsafe_take, exists, unsafe_borrow, try_unsafe_borrow}; /// Encapsulates a borrowed value. When this value goes out of scope, the /// pointer is returned. pub struct Borrowed { val: *const (), } #[unsafe_destructor] impl Drop for Borrowed { fn drop(&mut self) { unsafe { if self.val.is_null() { rtabort!("Aiee, returning null borrowed object!"); } let val: Box = mem::transmute(self.val); put::(val); rtassert!(exists()); } } } impl Deref for Borrowed { fn deref<'a>(&'a self) -> &'a T { unsafe { &*(self.val as *const T) } } } impl DerefMut for Borrowed { fn deref_mut<'a>(&'a mut self) -> &'a mut T { unsafe { &mut *(self.val as *mut T) } } } /// Borrow the thread-local value from thread-local storage. /// While the value is borrowed it is not available in TLS. /// /// # Safety note /// /// Does not validate the pointer type. #[inline] pub unsafe fn borrow() -> Borrowed { let val: *const () = mem::transmute(take::()); Borrowed { val: val, } } /// Compiled implementation of accessing the runtime local pointer. This is /// implemented using LLVM's thread_local attribute which isn't necessarily /// working on all platforms. This implementation is faster, however, so we use /// it wherever possible. #[cfg(not(windows), not(target_os = "android"), not(target_os = "ios"))] pub mod compiled { use core::prelude::*; use alloc::boxed::Box; use core::mem; #[cfg(test)] pub use realrustrt::shouldnt_be_public::RT_TLS_PTR; #[cfg(not(test))] #[thread_local] pub static mut RT_TLS_PTR: *mut u8 = 0 as *mut u8; pub fn init() {} pub unsafe fn cleanup() {} // Rationale for all of these functions being inline(never) // // The #[thread_local] annotation gets propagated all the way through to // LLVM, meaning the global is specially treated by LLVM to lower it to an // efficient sequence of instructions. This also involves dealing with fun // stuff in object files and whatnot. Regardless, it turns out this causes // trouble with green threads and lots of optimizations turned on. The // following case study was done on linux x86_64, but I would imagine that // other platforms are similar. // // On linux, the instruction sequence for loading the tls pointer global // looks like: // // mov %fs:0x0, %rax // mov -0x8(%rax), %rbx // // This code leads me to believe that (%fs:0x0) is a table, and then the // table contains the TLS values for the process. Hence, the slot at offset // -0x8 is the task TLS pointer. This leads us to the conclusion that this // table is the actual thread local part of each thread. The kernel sets up // the fs segment selector to point at the right region of memory for each // thread. // // Optimizations lead me to believe that this code is lowered to these // instructions in the LLVM codegen passes, because you'll see code like // this when everything is optimized: // // mov %fs:0x0, %r14 // mov -0x8(%r14), %rbx // // do something with %rbx, the rust Task pointer // // ... // <- do more things // // mov -0x8(%r14), %rbx // // do something else with %rbx // // Note that the optimization done here is that the first load is not // duplicated during the lower instructions. This means that the %fs:0x0 // memory location is only dereferenced once. // // Normally, this is actually a good thing! With green threads, however, // it's very possible for the code labeled "do more things" to context // switch to another thread. If this happens, then we *must* re-load %fs:0x0 // because it's changed (we're on a different thread). If we don't re-load // the table location, then we'll be reading the original thread's TLS // values, not our thread's TLS values. // // Hence, we never inline these functions. By never inlining, we're // guaranteed that loading the table is a local decision which is forced to // *always* happen. /// Give a pointer to thread-local storage. /// /// # Safety note /// /// Does not validate the pointer type. #[inline(never)] // see comments above pub unsafe fn put(sched: Box) { RT_TLS_PTR = mem::transmute(sched) } /// Take ownership of a pointer from thread-local storage. /// /// # Safety note /// /// Does not validate the pointer type. #[inline(never)] // see comments above pub unsafe fn take() -> Box { let ptr = RT_TLS_PTR; rtassert!(!ptr.is_null()); let ptr: Box = mem::transmute(ptr); // can't use `as`, due to type not matching with `cfg(test)` RT_TLS_PTR = mem::transmute(0u); ptr } /// Optionally take ownership of a pointer from thread-local storage. /// /// # Safety note /// /// Does not validate the pointer type. #[inline(never)] // see comments above pub unsafe fn try_take() -> Option> { let ptr = RT_TLS_PTR; if ptr.is_null() { None } else { let ptr: Box = mem::transmute(ptr); // can't use `as`, due to type not matching with `cfg(test)` RT_TLS_PTR = mem::transmute(0u); Some(ptr) } } /// Take ownership of a pointer from thread-local storage. /// /// # Safety note /// /// Does not validate the pointer type. /// Leaves the old pointer in TLS for speed. #[inline(never)] // see comments above pub unsafe fn unsafe_take() -> Box { mem::transmute(RT_TLS_PTR) } /// Check whether there is a thread-local pointer installed. #[inline(never)] // see comments above pub fn exists() -> bool { unsafe { RT_TLS_PTR.is_not_null() } } #[inline(never)] // see comments above pub unsafe fn unsafe_borrow() -> *mut T { if RT_TLS_PTR.is_null() { rtabort!("thread-local pointer is null. bogus!"); } RT_TLS_PTR as *mut T } #[inline(never)] // see comments above pub unsafe fn try_unsafe_borrow() -> Option<*mut T> { if RT_TLS_PTR.is_null() { None } else { Some(RT_TLS_PTR as *mut T) } } } /// Native implementation of having the runtime thread-local pointer. This /// implementation uses the `thread_local_storage` module to provide a /// thread-local value. pub mod native { use core::prelude::*; use alloc::boxed::Box; use core::mem; use core::ptr; use thread_local_storage as tls; static mut RT_TLS_KEY: tls::Key = -1; /// Initialize the TLS key. Other ops will fail if this isn't executed /// first. pub fn init() { unsafe { tls::create(&mut RT_TLS_KEY); } } pub unsafe fn cleanup() { rtassert!(RT_TLS_KEY != -1); tls::destroy(RT_TLS_KEY); } /// Give a pointer to thread-local storage. /// /// # Safety note /// /// Does not validate the pointer type. #[inline] pub unsafe fn put(sched: Box) { let key = tls_key(); let void_ptr: *mut u8 = mem::transmute(sched); tls::set(key, void_ptr); } /// Take ownership of a pointer from thread-local storage. /// /// # Safety note /// /// Does not validate the pointer type. #[inline] pub unsafe fn take() -> Box { let key = tls_key(); let void_ptr: *mut u8 = tls::get(key); if void_ptr.is_null() { rtabort!("thread-local pointer is null. bogus!"); } let ptr: Box = mem::transmute(void_ptr); tls::set(key, ptr::mut_null()); return ptr; } /// Optionally take ownership of a pointer from thread-local storage. /// /// # Safety note /// /// Does not validate the pointer type. #[inline] pub unsafe fn try_take() -> Option> { match maybe_tls_key() { Some(key) => { let void_ptr: *mut u8 = tls::get(key); if void_ptr.is_null() { None } else { let ptr: Box = mem::transmute(void_ptr); tls::set(key, ptr::mut_null()); Some(ptr) } } None => None } } /// Take ownership of a pointer from thread-local storage. /// /// # Safety note /// /// Does not validate the pointer type. /// Leaves the old pointer in TLS for speed. #[inline] pub unsafe fn unsafe_take() -> Box { let key = tls_key(); let void_ptr: *mut u8 = tls::get(key); if void_ptr.is_null() { rtabort!("thread-local pointer is null. bogus!"); } let ptr: Box = mem::transmute(void_ptr); return ptr; } /// Check whether there is a thread-local pointer installed. pub fn exists() -> bool { unsafe { match maybe_tls_key() { Some(key) => tls::get(key).is_not_null(), None => false } } } /// Borrow a mutable reference to the thread-local value /// /// # Safety Note /// /// Because this leaves the value in thread-local storage it is possible /// For the Scheduler pointer to be aliased pub unsafe fn unsafe_borrow() -> *mut T { let key = tls_key(); let void_ptr = tls::get(key); if void_ptr.is_null() { rtabort!("thread-local pointer is null. bogus!"); } void_ptr as *mut T } pub unsafe fn try_unsafe_borrow() -> Option<*mut T> { match maybe_tls_key() { Some(key) => { let void_ptr = tls::get(key); if void_ptr.is_null() { None } else { Some(void_ptr as *mut T) } } None => None } } #[inline] fn tls_key() -> tls::Key { match maybe_tls_key() { Some(key) => key, None => rtabort!("runtime tls key not initialized") } } #[inline] #[cfg(not(test))] #[allow(visible_private_types)] pub fn maybe_tls_key() -> Option { unsafe { // NB: This is a little racy because, while the key is // initialized under a mutex and it's assumed to be initialized // in the Scheduler ctor by any thread that needs to use it, // we are not accessing the key under a mutex. Threads that // are not using the new Scheduler but still *want to check* // whether they are running under a new Scheduler may see a 0 // value here that is in the process of being initialized in // another thread. I think this is fine since the only action // they could take if it was initialized would be to check the // thread-local value and see that it's not set. if RT_TLS_KEY != -1 { return Some(RT_TLS_KEY); } else { return None; } } } #[inline] #[cfg(test)] pub fn maybe_tls_key() -> Option { use realrustrt; unsafe { mem::transmute(realrustrt::shouldnt_be_public::maybe_tls_key()) } } }