From 568840707cd5f98a1970a44d632b833439d5312e Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 30 Sep 2016 20:13:58 +0000 Subject: std: Move elf TLS to sys::fast_thread_local --- src/libstd/thread/local.rs | 163 +-------------------------------------------- src/libstd/thread/mod.rs | 2 +- 2 files changed, 3 insertions(+), 162 deletions(-) (limited to 'src/libstd/thread') diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 54d3f793045..f74dd592495 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -166,8 +166,8 @@ macro_rules! __thread_local_inner { { #[thread_local] #[cfg(target_thread_local)] - static __KEY: $crate::thread::__ElfLocalKeyInner<$t> = - $crate::thread::__ElfLocalKeyInner::new(); + static __KEY: $crate::thread::__FastLocalKeyInner<$t> = + $crate::thread::__FastLocalKeyInner::new(); #[cfg(not(target_thread_local))] static __KEY: $crate::thread::__OsLocalKeyInner<$t> = @@ -310,165 +310,6 @@ impl LocalKey { } } -#[cfg(target_thread_local)] -#[doc(hidden)] -pub mod elf { - use cell::{Cell, UnsafeCell}; - use intrinsics; - use ptr; - - pub struct Key { - inner: UnsafeCell>, - - // Metadata to keep track of the state of the destructor. Remember that - // these variables are thread-local, not global. - dtor_registered: Cell, - dtor_running: Cell, - } - - unsafe impl ::marker::Sync for Key { } - - impl Key { - pub const fn new() -> Key { - Key { - inner: UnsafeCell::new(None), - dtor_registered: Cell::new(false), - dtor_running: Cell::new(false) - } - } - - pub fn get(&'static self) -> Option<&'static UnsafeCell>> { - unsafe { - if intrinsics::needs_drop::() && self.dtor_running.get() { - return None - } - self.register_dtor(); - } - Some(&self.inner) - } - - unsafe fn register_dtor(&self) { - if !intrinsics::needs_drop::() || self.dtor_registered.get() { - return - } - - register_dtor(self as *const _ as *mut u8, - destroy_value::); - self.dtor_registered.set(true); - } - } - - #[cfg(any(target_os = "linux", target_os = "fuchsia"))] - unsafe fn register_dtor_fallback(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { - // The fallback implementation uses a vanilla OS-based TLS key to track - // the list of destructors that need to be run for this thread. The key - // then has its own destructor which runs all the other destructors. - // - // The destructor for DTORS is a little special in that it has a `while` - // loop to continuously drain the list of registered destructors. It - // *should* be the case that this loop always terminates because we - // provide the guarantee that a TLS key cannot be set after it is - // flagged for destruction. - use sys_common::thread_local as os; - - static DTORS: os::StaticKey = os::StaticKey::new(Some(run_dtors)); - type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>; - if DTORS.get().is_null() { - let v: Box = box Vec::new(); - DTORS.set(Box::into_raw(v) as *mut u8); - } - let list: &mut List = &mut *(DTORS.get() as *mut List); - list.push((t, dtor)); - - unsafe extern fn run_dtors(mut ptr: *mut u8) { - while !ptr.is_null() { - let list: Box = Box::from_raw(ptr as *mut List); - for &(ptr, dtor) in list.iter() { - dtor(ptr); - } - ptr = DTORS.get(); - DTORS.set(ptr::null_mut()); - } - } - } - - // Since what appears to be glibc 2.18 this symbol has been shipped which - // GCC and clang both use to invoke destructors in thread_local globals, so - // let's do the same! - // - // Note, however, that we run on lots older linuxes, as well as cross - // compiling from a newer linux to an older linux, so we also have a - // fallback implementation to use as well. - // - // Due to rust-lang/rust#18804, make sure this is not generic! - #[cfg(target_os = "linux")] - unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { - use mem; - use libc; - - extern { - #[linkage = "extern_weak"] - static __dso_handle: *mut u8; - #[linkage = "extern_weak"] - static __cxa_thread_atexit_impl: *const libc::c_void; - } - if !__cxa_thread_atexit_impl.is_null() { - type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8), - arg: *mut u8, - dso_handle: *mut u8) -> libc::c_int; - mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl) - (dtor, t, &__dso_handle as *const _ as *mut _); - return - } - register_dtor_fallback(t, dtor); - } - - // OSX's analog of the above linux function is this _tlv_atexit function. - // The disassembly of thread_local globals in C++ (at least produced by - // clang) will have this show up in the output. - #[cfg(target_os = "macos")] - unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { - extern { - fn _tlv_atexit(dtor: unsafe extern fn(*mut u8), - arg: *mut u8); - } - _tlv_atexit(dtor, t); - } - - // Just use the thread_local fallback implementation, at least until there's - // a more direct implementation. - #[cfg(target_os = "fuchsia")] - unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { - register_dtor_fallback(t, dtor); - } - - pub unsafe extern fn destroy_value(ptr: *mut u8) { - let ptr = ptr as *mut Key; - // Right before we run the user destructor be sure to flag the - // destructor as running for this thread so calls to `get` will return - // `None`. - (*ptr).dtor_running.set(true); - - // The OSX implementation of TLS apparently had an odd aspect to it - // where the pointer we have may be overwritten while this destructor - // is running. Specifically if a TLS destructor re-accesses TLS it may - // trigger a re-initialization of all TLS variables, paving over at - // least some destroyed ones with initial values. - // - // This means that if we drop a TLS value in place on OSX that we could - // revert the value to its original state halfway through the - // destructor, which would be bad! - // - // Hence, we use `ptr::read` on OSX (to move to a "safe" location) - // instead of drop_in_place. - if cfg!(target_os = "macos") { - ptr::read((*ptr).inner.get()); - } else { - ptr::drop_in_place((*ptr).inner.get()); - } - } -} - #[doc(hidden)] pub mod os { use cell::{Cell, UnsafeCell}; diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 150482e4af4..4c3980b3c7b 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -183,7 +183,7 @@ pub use self::local::{LocalKey, LocalKeyState}; #[unstable(feature = "libstd_thread_internals", issue = "0")] #[cfg(target_thread_local)] -#[doc(hidden)] pub use self::local::elf::Key as __ElfLocalKeyInner; +#[doc(hidden)] pub use sys::fast_thread_local::Key as __FastLocalKeyInner; #[unstable(feature = "libstd_thread_internals", issue = "0")] #[doc(hidden)] pub use self::local::os::Key as __OsLocalKeyInner; -- cgit 1.4.1-3-g733a5 From 8b2600dbf9a02a19acc92db5d980986cad2ea38d Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 4 Oct 2016 18:00:27 +0000 Subject: Document reasoning for supporting both fast and OS TLS in the same bin --- src/libstd/thread/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/libstd/thread') diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 4c3980b3c7b..255cd2a9bc0 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -181,6 +181,15 @@ use time::Duration; #[stable(feature = "rust1", since = "1.0.0")] pub use self::local::{LocalKey, LocalKeyState}; +// The types used by the thread_local! macro to access TLS keys. Note that there +// are two types, the "OS" type and the "fast" type. The OS thread local key +// type is accessed via platform-specific API calls and is slow, while the fast +// key type is accessed via code generated via LLVM, where TLS keys are set up +// by the elf linker. Note that the OS TLS type is always available: on macOS +// the standard library is compiled with support for older platform versions +// where fast TLS was not available; end-user code is compiled with fast TLS +// where available, but both are needed. + #[unstable(feature = "libstd_thread_internals", issue = "0")] #[cfg(target_thread_local)] #[doc(hidden)] pub use sys::fast_thread_local::Key as __FastLocalKeyInner; -- cgit 1.4.1-3-g733a5