diff --git a/src/libstd/rt/local.rs b/src/libstd/rt/local.rs index 3e4072e617a..2375ce55766 100644 --- a/src/libstd/rt/local.rs +++ b/src/libstd/rt/local.rs @@ -132,7 +132,7 @@ mod test { #[test] fn thread_local_task_smoke_test() { do run_in_bare_thread { - local_ptr::init_tls_key(); + local_ptr::init(); let mut sched = ~new_test_uv_sched(); let task = ~Task::new_root(&mut sched.stack_pool, None, proc(){}); Local::put(task); @@ -144,7 +144,7 @@ mod test { #[test] fn thread_local_task_two_instances() { do run_in_bare_thread { - local_ptr::init_tls_key(); + local_ptr::init(); let mut sched = ~new_test_uv_sched(); let task = ~Task::new_root(&mut sched.stack_pool, None, proc(){}); Local::put(task); @@ -161,7 +161,7 @@ mod test { #[test] fn borrow_smoke_test() { do run_in_bare_thread { - local_ptr::init_tls_key(); + local_ptr::init(); let mut sched = ~new_test_uv_sched(); let task = ~Task::new_root(&mut sched.stack_pool, None, proc(){}); Local::put(task); @@ -177,7 +177,7 @@ mod test { #[test] fn borrow_with_return() { do run_in_bare_thread { - local_ptr::init_tls_key(); + local_ptr::init(); let mut sched = ~new_test_uv_sched(); let task = ~Task::new_root(&mut sched.stack_pool, None, proc(){}); Local::put(task); diff --git a/src/libstd/rt/local_ptr.rs b/src/libstd/rt/local_ptr.rs index 6355de36d43..c50a9778d33 100644 --- a/src/libstd/rt/local_ptr.rs +++ b/src/libstd/rt/local_ptr.rs @@ -15,183 +15,17 @@ //! XXX: Add runtime checks for usage of inconsistent pointer types. //! and for overwriting an existing pointer. -use libc::c_void; use cast; -#[cfg(stage0)] -#[cfg(windows)] -use ptr; use cell::Cell; -use option::{Option, Some, None}; use unstable::finally::Finally; -#[cfg(stage0)] -#[cfg(windows)] -use unstable::mutex::{Mutex, MUTEX_INIT}; -#[cfg(stage0)] -#[cfg(windows)] -use tls = rt::thread_local_storage; -#[cfg(not(stage0), not(windows), test)] -#[thread_local] -pub use realstd::rt::shouldnt_be_public::RT_TLS_PTR; +#[cfg(windows)] // mingw-w32 doesn't like thread_local things +#[cfg(target_os = "android")] // see #10686 +#[cfg(stage0)] // only remove this attribute after the next snapshot +pub use self::native::*; -#[cfg(not(stage0), not(windows), not(test))] -#[thread_local] -pub static mut RT_TLS_PTR: *mut c_void = 0 as *mut c_void; - -#[cfg(stage0)] -#[cfg(windows)] -static mut RT_TLS_KEY: tls::Key = -1; -#[cfg(stage0)] -#[cfg(windows)] -static mut tls_lock: Mutex = MUTEX_INIT; -static mut tls_initialized: bool = false; - -/// Initialize the TLS key. Other ops will fail if this isn't executed first. -#[inline(never)] -#[cfg(stage0)] -#[cfg(windows)] -pub fn init_tls_key() { - unsafe { - tls_lock.lock(); - if !tls_initialized { - tls::create(&mut RT_TLS_KEY); - tls_initialized = true; - } - tls_lock.unlock(); - } -} - -#[cfg(not(stage0), not(windows))] -pub fn init_tls_key() { - unsafe { - tls_initialized = true; - } -} - -#[cfg(windows)] -pub unsafe fn cleanup() { - // No real use to acquiring a lock around these operations. All we're - // going to do is destroy the lock anyway which races locking itself. This - // is why the whole function is labeled as 'unsafe' - assert!(tls_initialized); - tls::destroy(RT_TLS_KEY); - tls_lock.destroy(); - tls_initialized = false; -} - -#[cfg(not(windows))] -pub unsafe fn cleanup() { - assert!(tls_initialized); - tls_initialized = false; -} - -/// Give a pointer to thread-local storage. -/// -/// # Safety note -/// -/// Does not validate the pointer type. -#[inline] -#[cfg(stage0)] -#[cfg(windows)] -pub unsafe fn put(sched: ~T) { - let key = tls_key(); - let void_ptr: *mut c_void = cast::transmute(sched); - tls::set(key, void_ptr); -} - -/// Give a pointer to thread-local storage. -/// -/// # Safety note -/// -/// Does not validate the pointer type. -#[inline] -#[cfg(not(stage0), not(windows))] -pub unsafe fn put(sched: ~T) { - RT_TLS_PTR = cast::transmute(sched) -} - -/// Take ownership of a pointer from thread-local storage. -/// -/// # Safety note -/// -/// Does not validate the pointer type. -#[inline] -#[cfg(stage0)] -#[cfg(windows)] -pub unsafe fn take() -> ~T { - let key = tls_key(); - let void_ptr: *mut c_void = tls::get(key); - if void_ptr.is_null() { - rtabort!("thread-local pointer is null. bogus!"); - } - let ptr: ~T = cast::transmute(void_ptr); - tls::set(key, ptr::mut_null()); - return ptr; -} - -/// Take ownership of a pointer from thread-local storage. -/// -/// # Safety note -/// -/// Does not validate the pointer type. -#[inline] -#[cfg(not(stage0), not(windows))] -pub unsafe fn take() -> ~T { - let ptr: ~T = cast::transmute(RT_TLS_PTR); - RT_TLS_PTR = cast::transmute(0); // can't use `as`, due to type not matching with `cfg(test)` - 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] -#[cfg(stage0)] -#[cfg(windows)] -pub unsafe fn unsafe_take() -> ~T { - let key = tls_key(); - let void_ptr: *mut c_void = tls::get(key); - if void_ptr.is_null() { - rtabort!("thread-local pointer is null. bogus!"); - } - let ptr: ~T = cast::transmute(void_ptr); - return 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] -#[cfg(not(stage0), not(windows))] -pub unsafe fn unsafe_take() -> ~T { - cast::transmute(RT_TLS_PTR) -} - -/// Check whether there is a thread-local pointer installed. -#[cfg(stage0)] -#[cfg(windows)] -pub fn exists() -> bool { - unsafe { - match maybe_tls_key() { - Some(key) => tls::get(key).is_not_null(), - None => false - } - } -} - -/// Check whether there is a thread-local pointer installed. -#[cfg(not(stage0), not(windows))] -pub fn exists() -> bool { - unsafe { - RT_TLS_PTR.is_not_null() - } -} +#[cfg(not(stage0), not(windows), not(target_os = "android"))] +pub use self::compiled::*; /// Borrow the thread-local value from thread-local storage. /// While the value is borrowed it is not available in TLS. @@ -209,92 +43,239 @@ pub unsafe fn borrow(f: |&mut T|) { (|| f(unsafe_ptr)).finally(|| put(value_cell.take())); } -/// 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 -#[cfg(stage0)] -#[cfg(windows)] -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!"); +/// 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"))] +pub mod compiled { + use libc::c_void; + use cast; + use option::{Option, Some, None}; + + #[cfg(test)] + pub use realstd::rt::shouldnt_be_public::RT_TLS_PTR; + + #[cfg(not(test))] + #[thread_local] + pub static mut RT_TLS_PTR: *mut c_void = 0 as *mut c_void; + + pub fn init() {} + + pub unsafe fn cleanup() {} + + /// Give a pointer to thread-local storage. + /// + /// # Safety note + /// + /// Does not validate the pointer type. + #[inline] + pub unsafe fn put(sched: ~T) { + RT_TLS_PTR = cast::transmute(sched) + } + + /// Take ownership of a pointer from thread-local storage. + /// + /// # Safety note + /// + /// Does not validate the pointer type. + #[inline] + pub unsafe fn take() -> ~T { + let ptr: ~T = cast::transmute(RT_TLS_PTR); + // can't use `as`, due to type not matching with `cfg(test)` + RT_TLS_PTR = cast::transmute(0); + 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] + pub unsafe fn unsafe_take() -> ~T { + cast::transmute(RT_TLS_PTR) + } + + /// Check whether there is a thread-local pointer installed. + pub fn exists() -> bool { + unsafe { + RT_TLS_PTR.is_not_null() + } + } + + 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 + } + + pub unsafe fn try_unsafe_borrow() -> Option<*mut T> { + if RT_TLS_PTR.is_null() { + None + } else { + Some(RT_TLS_PTR as *mut T) + } } - void_ptr as *mut T } -#[cfg(not(stage0), not(windows))] -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 -} +/// 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 cast; + use libc::c_void; + use option::{Option, Some, None}; + use ptr; + use tls = rt::thread_local_storage; + use unstable::mutex::{Mutex, MUTEX_INIT}; -#[cfg(stage0)] -#[cfg(windows)] -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) + static mut LOCK: Mutex = MUTEX_INIT; + static mut INITIALIZED: bool = false; + 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 { + LOCK.lock(); + if !INITIALIZED { + tls::create(&mut RT_TLS_KEY); + INITIALIZED = true; + } + LOCK.unlock(); + } + } + + pub unsafe fn cleanup() { + assert!(INITIALIZED); + tls::destroy(RT_TLS_KEY); + LOCK.destroy(); + INITIALIZED = false; + } + + /// Give a pointer to thread-local storage. + /// + /// # Safety note + /// + /// Does not validate the pointer type. + #[inline] + pub unsafe fn put(sched: ~T) { + let key = tls_key(); + let void_ptr: *mut c_void = cast::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() -> ~T { + let key = tls_key(); + let void_ptr: *mut c_void = tls::get(key); + if void_ptr.is_null() { + rtabort!("thread-local pointer is null. bogus!"); + } + let ptr: ~T = cast::transmute(void_ptr); + tls::set(key, ptr::mut_null()); + return 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] + pub unsafe fn unsafe_take() -> ~T { + let key = tls_key(); + let void_ptr: *mut c_void = tls::get(key); + if void_ptr.is_null() { + rtabort!("thread-local pointer is null. bogus!"); + } + let ptr: ~T = cast::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 } } - None => None } -} -#[cfg(not(stage0), not(windows))] -pub unsafe fn try_unsafe_borrow() -> Option<*mut T> { - if RT_TLS_PTR.is_null() { - None - } else { - Some(RT_TLS_PTR as *mut T) + /// 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 } -} -#[inline] -#[cfg(stage0)] -#[cfg(windows)] -fn tls_key() -> tls::Key { - match maybe_tls_key() { - Some(key) => key, - None => rtabort!("runtime tls key not initialized") + 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] -#[cfg(not(test), stage0)] -#[cfg(not(test), windows)] -pub fn maybe_tls_key() -> Option { - unsafe { - // NB: This is a little racy because, while the key is - // initalized under a mutex and it's assumed to be initalized - // 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] + fn tls_key() -> tls::Key { + match maybe_tls_key() { + Some(key) => key, + None => rtabort!("runtime tls key not initialized") + } + } + + #[inline] + #[cfg(not(test))] + pub fn maybe_tls_key() -> Option { + unsafe { + // NB: This is a little racy because, while the key is + // initalized under a mutex and it's assumed to be initalized + // 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 realstd; + unsafe { + cast::transmute(realstd::rt::shouldnt_be_public::maybe_tls_key()) } } } - -#[inline] -#[cfg(test, stage0)] -#[cfg(test, windows)] -pub fn maybe_tls_key() -> Option { - unsafe { ::cast::transmute(::realstd::rt::shouldnt_be_public::maybe_tls_key()) } -} diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 79b7dbf2aab..0c69315b27d 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -95,11 +95,9 @@ pub use self::kill::BlockedTask; pub mod shouldnt_be_public { pub use super::select::SelectInner; pub use super::select::{SelectInner, SelectPortInner}; - #[cfg(stage0)] - #[cfg(windows)] - pub use super::local_ptr::maybe_tls_key; - #[cfg(not(stage0), not(windows))] - pub use super::local_ptr::RT_TLS_PTR; + pub use super::local_ptr::native::maybe_tls_key; + #[cfg(not(stage0), not(windows), not(target_os = "android"))] + pub use super::local_ptr::compiled::RT_TLS_PTR; } // Internal macros used by the runtime. diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs index 9a48fc51329..21753d9e4d9 100644 --- a/src/libstd/rt/sched.rs +++ b/src/libstd/rt/sched.rs @@ -172,7 +172,7 @@ impl Scheduler { self.idle_callback = Some(self.event_loop.pausible_idle_callback(cb)); // Initialize the TLS key. - local_ptr::init_tls_key(); + local_ptr::init(); // Create a task for the scheduler with an empty context. let sched_task = ~Task::new_sched_task();