1
Fork 0

std: Use cfg(target_thread_local) in thread_local!

This transitions the standard library's `thread_local!` macro to use the
freshly-added and gated `#[cfg(target_thread_local)]` attribute. This greatly
simplifies the `#[cfg]` logic in play here, but requires that the standard
library expose both the OS and ELF TLS implementation modules as unstable
implementation details.

The implementation details were shuffled around a bit but end up generally
compiling to the same thing.

Closes #26581 (this supersedes the need for the option)
Closes #27057 (this also starts ignoring the option)
This commit is contained in:
Alex Crichton 2015-12-11 12:42:29 -08:00
parent 617a7af704
commit cd74364e5d
4 changed files with 50 additions and 78 deletions

View file

@ -227,6 +227,7 @@
#![feature(borrow_state)] #![feature(borrow_state)]
#![feature(box_syntax)] #![feature(box_syntax)]
#![feature(cfg_target_vendor)] #![feature(cfg_target_vendor)]
#![feature(cfg_target_thread_local)]
#![feature(char_internals)] #![feature(char_internals)]
#![feature(clone_from_slice)] #![feature(clone_from_slice)]
#![feature(collections)] #![feature(collections)]

View file

@ -15,10 +15,6 @@
use cell::UnsafeCell; use cell::UnsafeCell;
use mem; 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. /// A thread local storage key which owns its contents.
/// ///
/// This key uses the fastest possible implementation available to it for the /// This key uses the fastest possible implementation available to it for the
@ -61,41 +57,27 @@ pub use self::imp::Key as __KeyInner;
/// }); /// });
/// ``` /// ```
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub struct LocalKey<T:'static> { pub struct LocalKey<T: 'static> {
// The key itself may be tagged with #[thread_local], and this `Key` is // This outer `LocalKey<T>` type is what's going to be stored in statics,
// stored as a `static`, and it's not valid for a static to reference the // but actual data inside will sometimes be tagged with #[thread_local].
// address of another thread_local static. For this reason we kinda wonkily // It's not valid for a true static to reference a #[thread_local] static,
// work around this by generating a shim function which will give us the // so we get around that by exposing an accessor through a layer of function
// address of the inner TLS key at runtime. // indirection (this thunk).
// //
// This is trivially devirtualizable by LLVM because we never store anything // Note that the thunk is itself unsafe because the returned lifetime of the
// to this field and rustc can declare the `static` as constant as well. // slot where data lives, `'static`, is not actually valid. The lifetime
inner: fn() -> &'static __KeyInner<T>, // 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<Option<T>>>,
// initialization routine to invoke to create a value // initialization routine to invoke to create a value
init: fn() -> T, 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`. /// Declare a new thread local storage key of type `std::thread::LocalKey`.
/// ///
/// See [LocalKey documentation](thread/struct.LocalKey.html) for more /// See [LocalKey documentation](thread/struct.LocalKey.html) for more
@ -103,36 +85,14 @@ pub struct LocalKey<T:'static> {
#[macro_export] #[macro_export]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable] #[allow_internal_unstable]
#[cfg(not(no_elf_tls))]
macro_rules! thread_local { macro_rules! thread_local {
(static $name:ident: $t:ty = $init:expr) => ( (static $name:ident: $t:ty = $init:expr) => (
static $name: $crate::thread::LocalKey<$t> = static $name: $crate::thread::LocalKey<$t> =
__thread_local_inner!($t, $init, __thread_local_inner!($t, $init);
#[cfg_attr(all(any(target_os = "macos", target_os = "linux"),
not(target_arch = "aarch64")),
thread_local)]);
); );
(pub static $name:ident: $t:ty = $init:expr) => ( (pub static $name:ident: $t:ty = $init:expr) => (
pub static $name: $crate::thread::LocalKey<$t> = pub static $name: $crate::thread::LocalKey<$t> =
__thread_local_inner!($t, $init, __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, #[]);
); );
} }
@ -143,12 +103,25 @@ macro_rules! thread_local {
#[macro_export] #[macro_export]
#[allow_internal_unstable] #[allow_internal_unstable]
macro_rules! __thread_local_inner { macro_rules! __thread_local_inner {
($t:ty, $init:expr, #[$($attr:meta),*]) => {{ ($t:ty, $init:expr) => {{
$(#[$attr])*
static __KEY: $crate::thread::__LocalKeyInner<$t> =
$crate::thread::__LocalKeyInner::new();
fn __init() -> $t { $init } 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) $crate::thread::LocalKey::new(__getit, __init)
}} }}
} }
@ -190,11 +163,11 @@ impl<T: 'static> LocalKey<T> {
#[unstable(feature = "thread_local_internals", #[unstable(feature = "thread_local_internals",
reason = "recently added to create a key", reason = "recently added to create a key",
issue = "0")] issue = "0")]
pub const fn new(inner: fn() -> &'static __KeyInner<T>, pub const fn new(inner: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>,
init: fn() -> T) -> LocalKey<T> { init: fn() -> T) -> LocalKey<T> {
LocalKey { LocalKey {
inner: inner, inner: inner,
init: init init: init,
} }
} }
@ -211,10 +184,10 @@ impl<T: 'static> LocalKey<T> {
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub fn with<F, R>(&'static self, f: F) -> R pub fn with<F, R>(&'static self, f: F) -> R
where F: FnOnce(&T) -> R { where F: FnOnce(&T) -> R {
let slot = (self.inner)();
unsafe { unsafe {
let slot = slot.get().expect("cannot access a TLS value during or \ let slot = (self.inner)();
after it is destroyed"); let slot = slot.expect("cannot access a TLS value during or \
after it is destroyed");
f(match *slot.get() { f(match *slot.get() {
Some(ref inner) => inner, Some(ref inner) => inner,
None => self.init(slot), None => self.init(slot),
@ -270,7 +243,7 @@ impl<T: 'static> LocalKey<T> {
issue = "27716")] issue = "27716")]
pub fn state(&'static self) -> LocalKeyState { pub fn state(&'static self) -> LocalKeyState {
unsafe { unsafe {
match (self.inner)().get() { match (self.inner)() {
Some(cell) => { Some(cell) => {
match *cell.get() { match *cell.get() {
Some(..) => LocalKeyState::Valid, Some(..) => LocalKeyState::Valid,
@ -283,11 +256,9 @@ impl<T: 'static> LocalKey<T> {
} }
} }
#[cfg(all(any(target_os = "macos", target_os = "linux"), #[cfg(target_thread_local)]
not(target_arch = "aarch64"),
not(no_elf_tls)))]
#[doc(hidden)] #[doc(hidden)]
mod imp { pub mod elf {
use cell::{Cell, UnsafeCell}; use cell::{Cell, UnsafeCell};
use intrinsics; use intrinsics;
use ptr; use ptr;
@ -431,11 +402,8 @@ mod imp {
} }
} }
#[cfg(any(not(any(target_os = "macos", target_os = "linux")),
target_arch = "aarch64",
no_elf_tls))]
#[doc(hidden)] #[doc(hidden)]
mod imp { pub mod os {
use prelude::v1::*; use prelude::v1::*;
use cell::{Cell, UnsafeCell}; use cell::{Cell, UnsafeCell};

View file

@ -191,7 +191,10 @@ pub use self::local::{LocalKey, LocalKeyState};
pub use self::scoped_tls::ScopedKey; pub use self::scoped_tls::ScopedKey;
#[unstable(feature = "libstd_thread_internals", issue = "0")] #[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")] #[unstable(feature = "libstd_thread_internals", issue = "0")]
#[doc(hidden)] pub use self::scoped_tls::__KeyInner as __ScopedKeyInner; #[doc(hidden)] pub use self::scoped_tls::__KeyInner as __ScopedKeyInner;

View file

@ -238,7 +238,7 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
("type_ascription", "1.6.0", Some(23416), Active), ("type_ascription", "1.6.0", Some(23416), Active),
// Allows cfg(target_thread_local) // 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) // (changing above list without updating src/doc/reference.md makes @cmr sad)