]> git.lizzy.rs Git - rust.git/commitdiff
Use the native tls implementation on android
authorAlex Crichton <alex@alexcrichton.com>
Wed, 27 Nov 2013 04:23:56 +0000 (20:23 -0800)
committerAlex Crichton <alex@alexcrichton.com>
Wed, 27 Nov 2013 19:56:43 +0000 (11:56 -0800)
Turns out android doesn't support LLVM's thread_local attribute and accompanying
implementation.

Closes #10686

src/libstd/rt/local.rs
src/libstd/rt/local_ptr.rs
src/libstd/rt/mod.rs
src/libstd/rt/sched.rs

index 3e4072e617a4e1656ea31cb7052bf1e0a526d351..2375ce55766806969d633145e7449406a4505f80 100644 (file)
@@ -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 @@ fn thread_local_task_smoke_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 @@ fn thread_local_task_two_instances() {
     #[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 @@ fn borrow_smoke_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);
index 6355de36d43bb5d857dd9cb25b43ded57379ccce..c50a9778d33cf11dfd4448d390eb5784959955a6 100644 (file)
 //! 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<T>(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<T>(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<T>(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>() -> ~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<T>(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>() -> ~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>() -> ~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>() -> ~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>() -> ~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>() -> ~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<T>() -> *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<T>() -> 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<T>(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<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!");
+    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<T>() -> *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<T>(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<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)
+    /// 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, stage0)]
-#[cfg(test, windows)]
-pub fn maybe_tls_key() -> Option<tls::Key> {
-    unsafe { ::cast::transmute(::realstd::rt::shouldnt_be_public::maybe_tls_key()) }
+    #[inline] #[cfg(test)]
+    pub fn maybe_tls_key() -> Option<tls::Key> {
+        use realstd;
+        unsafe {
+            cast::transmute(realstd::rt::shouldnt_be_public::maybe_tls_key())
+        }
+    }
 }
index 79b7dbf2aabf4f42fe47b51f4c9bc15672b4d553..0c69315b27dca919969ef7d90ed2f5c8f9493b72 100644 (file)
 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.
index 9a48fc51329994773d0a923bfe7c08a8398060ab..21753d9e4d99ec3d8211a2f32a8ffc34db9c6df9 100644 (file)
@@ -172,7 +172,7 @@ pub fn bootstrap(mut ~self, task: ~Task) {
         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();