diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index eba0c799cd2..01bf5147557 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -227,6 +227,7 @@ #![feature(borrow_state)] #![feature(box_syntax)] #![feature(cfg_target_vendor)] +#![feature(cfg_target_thread_local)] #![feature(char_internals)] #![feature(clone_from_slice)] #![feature(collections)] diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 870247f7e82..ca0f1031562 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -15,10 +15,6 @@ use cell::UnsafeCell; use mem; -// Sure wish we had macro hygiene, no? -#[doc(hidden)] -pub use self::imp::Key as __KeyInner; - /// A thread local storage key which owns its contents. /// /// This key uses the fastest possible implementation available to it for the @@ -61,41 +57,27 @@ /// }); /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub struct LocalKey { - // The key itself may be tagged with #[thread_local], and this `Key` is - // stored as a `static`, and it's not valid for a static to reference the - // address of another thread_local static. For this reason we kinda wonkily - // work around this by generating a shim function which will give us the - // address of the inner TLS key at runtime. +pub struct LocalKey { + // This outer `LocalKey` type is what's going to be stored in statics, + // but actual data inside will sometimes be tagged with #[thread_local]. + // It's not valid for a true static to reference a #[thread_local] static, + // so we get around that by exposing an accessor through a layer of function + // indirection (this thunk). // - // This is trivially devirtualizable by LLVM because we never store anything - // to this field and rustc can declare the `static` as constant as well. - inner: fn() -> &'static __KeyInner, + // Note that the thunk is itself unsafe because the returned lifetime of the + // slot where data lives, `'static`, is not actually valid. The lifetime + // here is actually `'thread`! + // + // Although this is an extra layer of indirection, it should in theory be + // trivially devirtualizable by LLVM because the value of `inner` never + // changes and the constant should be readonly within a crate. This mainly + // only runs into problems when TLS statics are exported across crates. + inner: unsafe fn() -> Option<&'static UnsafeCell>>, // initialization routine to invoke to create a value init: fn() -> T, } -// Macro pain #4586: -// -// When cross compiling, rustc will load plugins and macros from the *host* -// platform before search for macros from the target platform. This is primarily -// done to detect, for example, plugins. Ideally the macro below would be -// defined once per module below, but unfortunately this means we have the -// following situation: -// -// 1. We compile libstd for x86_64-unknown-linux-gnu, this thread_local!() macro -// will inject #[thread_local] statics. -// 2. We then try to compile a program for arm-linux-androideabi -// 3. The compiler has a host of linux and a target of android, so it loads -// macros from the *linux* libstd. -// 4. The macro generates a #[thread_local] field, but the android libstd does -// not use #[thread_local] -// 5. Compile error about structs with wrong fields. -// -// To get around this, we're forced to inject the #[cfg] logic into the macro -// itself. Woohoo. - /// Declare a new thread local storage key of type `std::thread::LocalKey`. /// /// See [LocalKey documentation](thread/struct.LocalKey.html) for more @@ -103,36 +85,14 @@ pub struct LocalKey { #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable] -#[cfg(not(no_elf_tls))] macro_rules! thread_local { (static $name:ident: $t:ty = $init:expr) => ( static $name: $crate::thread::LocalKey<$t> = - __thread_local_inner!($t, $init, - #[cfg_attr(all(any(target_os = "macos", target_os = "linux"), - not(target_arch = "aarch64")), - thread_local)]); + __thread_local_inner!($t, $init); ); (pub static $name:ident: $t:ty = $init:expr) => ( pub static $name: $crate::thread::LocalKey<$t> = - __thread_local_inner!($t, $init, - #[cfg_attr(all(any(target_os = "macos", target_os = "linux"), - not(target_arch = "aarch64")), - thread_local)]); - ); -} - -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -#[allow_internal_unstable] -#[cfg(no_elf_tls)] -macro_rules! thread_local { - (static $name:ident: $t:ty = $init:expr) => ( - static $name: $crate::thread::LocalKey<$t> = - __thread_local_inner!($t, $init, #[]); - ); - (pub static $name:ident: $t:ty = $init:expr) => ( - pub static $name: $crate::thread::LocalKey<$t> = - __thread_local_inner!($t, $init, #[]); + __thread_local_inner!($t, $init); ); } @@ -143,12 +103,25 @@ macro_rules! thread_local { #[macro_export] #[allow_internal_unstable] macro_rules! __thread_local_inner { - ($t:ty, $init:expr, #[$($attr:meta),*]) => {{ - $(#[$attr])* - static __KEY: $crate::thread::__LocalKeyInner<$t> = - $crate::thread::__LocalKeyInner::new(); + ($t:ty, $init:expr) => {{ fn __init() -> $t { $init } - fn __getit() -> &'static $crate::thread::__LocalKeyInner<$t> { &__KEY } + + unsafe fn __getit() -> $crate::option::Option< + &'static $crate::cell::UnsafeCell< + $crate::option::Option<$t>>> + { + #[thread_local] + #[cfg(target_thread_local)] + static __KEY: $crate::thread::__ElfLocalKeyInner<$t> = + $crate::thread::__ElfLocalKeyInner::new(); + + #[cfg(not(target_thread_local))] + static __KEY: $crate::thread::__OsLocalKeyInner<$t> = + $crate::thread::__OsLocalKeyInner::new(); + + __KEY.get() + } + $crate::thread::LocalKey::new(__getit, __init) }} } @@ -190,11 +163,11 @@ impl LocalKey { #[unstable(feature = "thread_local_internals", reason = "recently added to create a key", issue = "0")] - pub const fn new(inner: fn() -> &'static __KeyInner, + pub const fn new(inner: unsafe fn() -> Option<&'static UnsafeCell>>, init: fn() -> T) -> LocalKey { LocalKey { inner: inner, - init: init + init: init, } } @@ -211,10 +184,10 @@ pub const fn new(inner: fn() -> &'static __KeyInner, #[stable(feature = "rust1", since = "1.0.0")] pub fn with(&'static self, f: F) -> R where F: FnOnce(&T) -> R { - let slot = (self.inner)(); unsafe { - let slot = slot.get().expect("cannot access a TLS value during or \ - after it is destroyed"); + let slot = (self.inner)(); + let slot = slot.expect("cannot access a TLS value during or \ + after it is destroyed"); f(match *slot.get() { Some(ref inner) => inner, None => self.init(slot), @@ -270,7 +243,7 @@ unsafe fn init(&self, slot: &UnsafeCell>) -> &T { issue = "27716")] pub fn state(&'static self) -> LocalKeyState { unsafe { - match (self.inner)().get() { + match (self.inner)() { Some(cell) => { match *cell.get() { Some(..) => LocalKeyState::Valid, @@ -283,11 +256,9 @@ pub fn state(&'static self) -> LocalKeyState { } } -#[cfg(all(any(target_os = "macos", target_os = "linux"), - not(target_arch = "aarch64"), - not(no_elf_tls)))] +#[cfg(target_thread_local)] #[doc(hidden)] -mod imp { +pub mod elf { use cell::{Cell, UnsafeCell}; use intrinsics; use ptr; @@ -431,11 +402,8 @@ fn _tlv_atexit(dtor: unsafe extern fn(*mut u8), } } -#[cfg(any(not(any(target_os = "macos", target_os = "linux")), - target_arch = "aarch64", - no_elf_tls))] #[doc(hidden)] -mod imp { +pub mod os { use prelude::v1::*; use cell::{Cell, UnsafeCell}; diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index d4f04d5d94f..0e525f39421 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -191,7 +191,10 @@ pub use self::scoped_tls::ScopedKey; #[unstable(feature = "libstd_thread_internals", issue = "0")] -#[doc(hidden)] pub use self::local::__KeyInner as __LocalKeyInner; +#[cfg(target_thread_local)] +#[doc(hidden)] pub use self::local::elf::Key as __ElfLocalKeyInner; +#[unstable(feature = "libstd_thread_internals", issue = "0")] +#[doc(hidden)] pub use self::local::os::Key as __OsLocalKeyInner; #[unstable(feature = "libstd_thread_internals", issue = "0")] #[doc(hidden)] pub use self::scoped_tls::__KeyInner as __ScopedKeyInner; diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index caad7c6e7f5..4ea0fd76fea 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -238,7 +238,7 @@ ("type_ascription", "1.6.0", Some(23416), Active), // Allows cfg(target_thread_local) - ("cfg_target_thread_local", "1.7.0", Some(26581), Active), + ("cfg_target_thread_local", "1.7.0", Some(29594), Active), ]; // (changing above list without updating src/doc/reference.md makes @cmr sad)