From 1686bfabf5dce46c74c04c28efecf4488d674536 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 26 Nov 2013 20:23:56 -0800 Subject: [PATCH] Use the native tls implementation on android Turns out android doesn't support LLVM's thread_local attribute and accompanying implementation. Closes #10686 --- src/libstd/rt/local.rs | 8 +- src/libstd/rt/local_ptr.rs | 457 ++++++++++++++++++------------------- src/libstd/rt/mod.rs | 8 +- src/libstd/rt/sched.rs | 2 +- 4 files changed, 227 insertions(+), 248 deletions(-) diff --git a/src/libstd/rt/local.rs b/src/libstd/rt/local.rs index 3e4072e617a4e..2375ce5576680 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 6355de36d43bb..c50a9778d33cf 100644 --- a/src/libstd/rt/local_ptr.rs +++ b/src/libstd/rt/local_ptr.rs @@ -15,286 +15,267 @@ //! 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(not(stage0), not(windows), not(target_os = "android"))] +pub use self::compiled::*; -#[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; +/// 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. +pub unsafe fn borrow(f: |&mut T|) { + let mut value = take(); -/// 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(); - } -} + // XXX: Need a different abstraction from 'finally' here to avoid unsafety + let unsafe_ptr = cast::transmute_mut_region(&mut *value); + let value_cell = Cell::new(value); -#[cfg(not(stage0), not(windows))] -pub fn init_tls_key() { - unsafe { - tls_initialized = true; - } + (|| f(unsafe_ptr)).finally(|| put(value_cell.take())); } -#[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; -} +/// 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(not(windows))] -pub unsafe fn cleanup() { - assert!(tls_initialized); - tls_initialized = false; -} + #[cfg(test)] + pub use realstd::rt::shouldnt_be_public::RT_TLS_PTR; -/// 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); -} + #[cfg(not(test))] + #[thread_local] + pub static mut RT_TLS_PTR: *mut c_void = 0 as *mut c_void; -/// 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) -} + pub fn init() {} -/// 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!"); + 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) } - 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. + #[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] -#[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!"); + /// 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) } - 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. + pub fn exists() -> bool { + unsafe { + RT_TLS_PTR.is_not_null() + } + } -/// 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 + 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 } -} -/// 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() + 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 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. -pub unsafe fn borrow(f: |&mut T|) { - let mut value = take(); +/// 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}; - // XXX: Need a different abstraction from 'finally' here to avoid unsafety - let unsafe_ptr = cast::transmute_mut_region(&mut *value); - let value_cell = Cell::new(value); + static mut LOCK: Mutex = MUTEX_INIT; + static mut INITIALIZED: bool = false; + static mut RT_TLS_KEY: tls::Key = -1; - (|| f(unsafe_ptr)).finally(|| put(value_cell.take())); -} + /// 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(); + } + } -/// 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!"); + pub unsafe fn cleanup() { + assert!(INITIALIZED); + tls::destroy(RT_TLS_KEY); + LOCK.destroy(); + INITIALIZED = false; } - 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!"); + /// 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); } - RT_TLS_PTR as *mut T -} -#[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) + /// 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, stage0)] -#[cfg(test, windows)] -pub fn maybe_tls_key() -> Option { - unsafe { ::cast::transmute(::realstd::rt::shouldnt_be_public::maybe_tls_key()) } + #[inline] #[cfg(test)] + pub fn maybe_tls_key() -> Option { + use realstd; + 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 79b7dbf2aabf4..0c69315b27dca 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 9a48fc5132999..21753d9e4d99e 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();