auto merge of #10687 : alexcrichton/rust/issue-10686, r=thestinger
Turns out android doesn't support LLVM's thread_local attribute and accompanying implementation. Closes #10686
This commit is contained in:
commit
db5b51ae63
@ -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);
|
||||
|
@ -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<T>(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<T>(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>() -> ~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>() -> ~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>() -> ~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>() -> ~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<T>(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<T>() -> *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<T>(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>() -> ~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>() -> ~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<T>() -> *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<T>() -> 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<T>() -> *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<T>() -> 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<T>(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>() -> ~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>() -> ~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<T>() -> 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<T>() -> *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<T>() -> 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<tls::Key> {
|
||||
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<tls::Key> {
|
||||
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<tls::Key> {
|
||||
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<tls::Key> {
|
||||
unsafe { ::cast::transmute(::realstd::rt::shouldnt_be_public::maybe_tls_key()) }
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user