Skip to content

std: Second pass stabilization for thread_local #20354

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 2, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/libstd/sys/common/thread_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@

use core::prelude::*;

use thread::Thread;
use cell::RefCell;
use string::String;
use thread::Thread;
use thread_local::State;

struct ThreadInfo {
// This field holds the known bounds of the stack in (lo, hi)
Expand All @@ -27,7 +28,7 @@ thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = RefCell::new(N

impl ThreadInfo {
fn with<R>(f: |&mut ThreadInfo| -> R) -> R {
if THREAD_INFO.destroyed() {
if THREAD_INFO.state() == State::Destroyed {
panic!("Use of std::thread::Thread::current() is not possible after \
the thread's local data has been destroyed");
}
Expand Down
159 changes: 127 additions & 32 deletions src/libstd/thread_local/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,23 @@
//! `Cell` or `RefCell` types.

#![macro_escape]
#![experimental]
#![stable]

use prelude::*;

use cell::UnsafeCell;

// Sure wish we had macro hygiene, no?
#[doc(hidden)] pub use self::imp::Key as KeyInner;
#[doc(hidden)] pub use self::imp::destroy_value;
#[doc(hidden)] pub use sys_common::thread_local::INIT_INNER as OS_INIT_INNER;
#[doc(hidden)] pub use sys_common::thread_local::StaticKey as OsStaticKey;

pub mod scoped;

// Sure wish we had macro hygiene, no?
#[doc(hidden)]
pub mod __impl {
pub use super::imp::Key as KeyInner;
pub use super::imp::destroy_value;
pub use sys_common::thread_local::INIT_INNER as OS_INIT_INNER;
pub use sys_common::thread_local::StaticKey as OsStaticKey;
}

/// A thread local storage key which owns its contents.
///
/// This key uses the fastest possible implementation available to it for the
Expand Down Expand Up @@ -90,6 +93,7 @@ pub mod scoped;
/// assert_eq!(*f.borrow(), 2);
/// });
/// ```
#[stable]
pub struct Key<T> {
// 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
Expand All @@ -100,7 +104,7 @@ pub struct Key<T> {
// This is trivially devirtualizable by LLVM because we never store anything
// to this field and rustc can declare the `static` as constant as well.
#[doc(hidden)]
pub inner: fn() -> &'static KeyInner<UnsafeCell<Option<T>>>,
pub inner: fn() -> &'static __impl::KeyInner<UnsafeCell<Option<T>>>,

// initialization routine to invoke to create a value
#[doc(hidden)]
Expand All @@ -109,12 +113,12 @@ pub struct Key<T> {

/// Declare a new thread local storage key of type `std::thread_local::Key`.
#[macro_export]
#[doc(hidden)]
#[stable]
macro_rules! thread_local {
(static $name:ident: $t:ty = $init:expr) => (
static $name: ::std::thread_local::Key<$t> = {
use std::cell::UnsafeCell as __UnsafeCell;
use std::thread_local::KeyInner as __KeyInner;
use std::thread_local::__impl::KeyInner as __KeyInner;
use std::option::Option as __Option;
use std::option::Option::None as __None;

Expand All @@ -131,7 +135,7 @@ macro_rules! thread_local {
(pub static $name:ident: $t:ty = $init:expr) => (
pub static $name: ::std::thread_local::Key<$t> = {
use std::cell::UnsafeCell as __UnsafeCell;
use std::thread_local::KeyInner as __KeyInner;
use std::thread_local::__impl::KeyInner as __KeyInner;
use std::option::Option as __Option;
use std::option::Option::None as __None;

Expand Down Expand Up @@ -168,46 +172,77 @@ macro_rules! thread_local {
// itself. Woohoo.

#[macro_export]
#[doc(hidden)]
macro_rules! __thread_local_inner {
(static $name:ident: $t:ty = $init:expr) => (
#[cfg_attr(any(target_os = "macos", target_os = "linux"), thread_local)]
static $name: ::std::thread_local::KeyInner<$t> =
static $name: ::std::thread_local::__impl::KeyInner<$t> =
__thread_local_inner!($init, $t);
);
(pub static $name:ident: $t:ty = $init:expr) => (
#[cfg_attr(any(target_os = "macos", target_os = "linux"), thread_local)]
pub static $name: ::std::thread_local::KeyInner<$t> =
pub static $name: ::std::thread_local::__impl::KeyInner<$t> =
__thread_local_inner!($init, $t);
);
($init:expr, $t:ty) => ({
#[cfg(any(target_os = "macos", target_os = "linux"))]
const INIT: ::std::thread_local::KeyInner<$t> = {
::std::thread_local::KeyInner {
const _INIT: ::std::thread_local::__impl::KeyInner<$t> = {
::std::thread_local::__impl::KeyInner {
inner: ::std::cell::UnsafeCell { value: $init },
dtor_registered: ::std::cell::UnsafeCell { value: false },
dtor_running: ::std::cell::UnsafeCell { value: false },
}
};

#[cfg(all(not(any(target_os = "macos", target_os = "linux"))))]
const INIT: ::std::thread_local::KeyInner<$t> = {
const _INIT: ::std::thread_local::__impl::KeyInner<$t> = {
unsafe extern fn __destroy(ptr: *mut u8) {
::std::thread_local::destroy_value::<$t>(ptr);
::std::thread_local::__impl::destroy_value::<$t>(ptr);
}

::std::thread_local::KeyInner {
::std::thread_local::__impl::KeyInner {
inner: ::std::cell::UnsafeCell { value: $init },
os: ::std::thread_local::OsStaticKey {
inner: ::std::thread_local::OS_INIT_INNER,
os: ::std::thread_local::__impl::OsStaticKey {
inner: ::std::thread_local::__impl::OS_INIT_INNER,
dtor: ::std::option::Option::Some(__destroy as unsafe extern fn(*mut u8)),
},
}
};

INIT
_INIT
});
}

/// Indicator of the state of a thread local storage key.
#[unstable = "state querying was recently added"]
#[deriving(Eq, PartialEq, Copy)]
pub enum State {
/// All keys are in this state whenever a thread starts. Keys will
/// transition to the `Valid` state once the first call to `with` happens
/// and the initialization expression succeeds.
///
/// Keys in the `Uninitialized` state will yield a reference to the closure
/// passed to `with` so long as the initialization routine does not panic.
Uninitialized,

/// Once a key has been accessed successfully, it will enter the `Valid`
/// state. Keys in the `Valid` state will remain so until the thread exits,
/// at which point the destructor will be run and the key will enter the
/// `Destroyed` state.
///
/// Keys in the `Valid` state will be guaranteed to yield a reference to the
/// closure passed to `with`.
Valid,

/// When a thread exits, the destructors for keys will be run (if
/// necessary). While a destructor is running, and possibly after a
/// destructor has run, a key is in the `Destroyed` state.
///
/// Keys in the `Destroyed` states will trigger a panic when accessed via
/// `with`.
Destroyed,
}

impl<T: 'static> Key<T> {
/// Acquire a reference to the value in this TLS key.
///
Expand All @@ -219,6 +254,7 @@ impl<T: 'static> Key<T> {
/// This function will `panic!()` if the key currently has its
/// destructor running, and it **may** panic if the destructor has
/// previously been run for this thread.
#[stable]
pub fn with<F, R>(&'static self, f: F) -> R
where F: FnOnce(&T) -> R {
let slot = (self.inner)();
Expand All @@ -233,17 +269,52 @@ impl<T: 'static> Key<T> {
}

unsafe fn init(&self, slot: &UnsafeCell<Option<T>>) -> &T {
*slot.get() = Some((self.init)());
(*slot.get()).as_ref().unwrap()
// Execute the initialization up front, *then* move it into our slot,
// just in case initialization fails.
let value = (self.init)();
let ptr = slot.get();
*ptr = Some(value);
(*ptr).as_ref().unwrap()
}

/// Test this TLS key to determine whether its value has been destroyed for
/// the current thread or not.
/// Query the current state of this key.
///
/// A key is initially in the `Uninitialized` state whenever a thread
/// starts. It will remain in this state up until the first call to `with`
/// within a thread has run the initialization expression successfully.
///
/// Once the initialization expression succeeds, the key transitions to the
/// `Valid` state which will guarantee that future calls to `with` will
/// succeed within the thread.
///
/// This will not initialize the key if it is not already initialized.
pub fn destroyed(&'static self) -> bool {
unsafe { (self.inner)().get().is_none() }
/// When a thread exits, each key will be destroyed in turn, and as keys are
/// destroyed they will enter the `Destroyed` state just before the
/// destructor starts to run. Keys may remain in the `Destroyed` state after
/// destruction has completed. Keys without destructors (e.g. with types
/// that are `Copy`), may never enter the `Destroyed` state.
///
/// Keys in the `Uninitialized` can be accessed so long as the
/// initialization does not panic. Keys in the `Valid` state are guaranteed
/// to be able to be accessed. Keys in the `Destroyed` state will panic on
/// any call to `with`.
#[unstable = "state querying was recently added"]
pub fn state(&'static self) -> State {
unsafe {
match (self.inner)().get() {
Some(cell) => {
match *cell.get() {
Some(..) => State::Valid,
None => State::Uninitialized,
}
}
None => State::Destroyed,
}
}
}

/// Deprecated
#[deprecated = "function renamed to state() and returns more info"]
pub fn destroyed(&'static self) -> bool { self.state() == State::Destroyed }
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
Expand Down Expand Up @@ -456,6 +527,7 @@ mod tests {
use prelude::*;

use cell::UnsafeCell;
use super::State;
use thread::Thread;

struct Foo(Sender<()>);
Expand Down Expand Up @@ -489,6 +561,29 @@ mod tests {
});
}

#[test]
fn states() {
struct Foo;
impl Drop for Foo {
fn drop(&mut self) {
assert!(FOO.state() == State::Destroyed);
}
}
fn foo() -> Foo {
assert!(FOO.state() == State::Uninitialized);
Foo
}
thread_local!(static FOO: Foo = foo());

Thread::spawn(|| {
assert!(FOO.state() == State::Uninitialized);
FOO.with(|_| {
assert!(FOO.state() == State::Valid);
});
assert!(FOO.state() == State::Valid);
}).join().ok().unwrap();
}

#[test]
fn smoke_dtor() {
thread_local!(static FOO: UnsafeCell<Option<Foo>> = UnsafeCell {
Expand Down Expand Up @@ -521,7 +616,7 @@ mod tests {
fn drop(&mut self) {
unsafe {
HITS += 1;
if K2.destroyed() {
if K2.state() == State::Destroyed {
assert_eq!(HITS, 3);
} else {
if HITS == 1 {
Expand All @@ -537,7 +632,7 @@ mod tests {
fn drop(&mut self) {
unsafe {
HITS += 1;
assert!(!K1.destroyed());
assert!(K1.state() != State::Destroyed);
assert_eq!(HITS, 2);
K1.with(|s| *s.get() = Some(S1));
}
Expand All @@ -558,7 +653,7 @@ mod tests {

impl Drop for S1 {
fn drop(&mut self) {
assert!(K1.destroyed());
assert!(K1.state() == State::Destroyed);
}
}

Expand All @@ -581,7 +676,7 @@ mod tests {
fn drop(&mut self) {
let S1(ref tx) = *self;
unsafe {
if !K2.destroyed() {
if K2.state() != State::Destroyed {
K2.with(|s| *s.get() = Some(Foo(tx.clone())));
}
}
Expand Down
25 changes: 15 additions & 10 deletions src/libstd/thread_local/scoped.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,17 @@
//! ```

#![macro_escape]
#![unstable = "scoped TLS has yet to have wide enough use to fully consider \
stabilizing its interface"]

use prelude::*;

// macro hygiene sure would be nice, wouldn't it?
#[doc(hidden)] pub use self::imp::KeyInner;
#[doc(hidden)] pub use sys_common::thread_local::INIT as OS_INIT;
#[doc(hidden)]
pub mod __impl {
pub use super::imp::KeyInner;
pub use sys_common::thread_local::INIT as OS_INIT;
}

/// Type representing a thread local storage key corresponding to a reference
/// to the type parameter `T`.
Expand All @@ -53,7 +58,7 @@ use prelude::*;
/// type `T` scoped to a particular lifetime. Keys provides two methods, `set`
/// and `with`, both of which currently use closures to control the scope of
/// their contents.
pub struct Key<T> { #[doc(hidden)] pub inner: KeyInner<T> }
pub struct Key<T> { #[doc(hidden)] pub inner: __impl::KeyInner<T> }

/// Declare a new scoped thread local storage key.
///
Expand Down Expand Up @@ -88,21 +93,21 @@ macro_rules! __scoped_thread_local_inner {
use std::thread_local::scoped::Key as __Key;

#[cfg(not(any(windows, target_os = "android", target_os = "ios")))]
const INIT: __Key<$t> = __Key {
inner: ::std::thread_local::scoped::KeyInner {
const _INIT: __Key<$t> = __Key {
inner: ::std::thread_local::scoped::__impl::KeyInner {
inner: ::std::cell::UnsafeCell { value: 0 as *mut _ },
}
};

#[cfg(any(windows, target_os = "android", target_os = "ios"))]
const INIT: __Key<$t> = __Key {
inner: ::std::thread_local::scoped::KeyInner {
inner: ::std::thread_local::scoped::OS_INIT,
const _INIT: __Key<$t> = __Key {
inner: ::std::thread_local::scoped::__impl::KeyInner {
inner: ::std::thread_local::scoped::__impl::OS_INIT,
marker: ::std::kinds::marker::InvariantType,
}
};

INIT
_INIT
})
}

Expand Down Expand Up @@ -139,7 +144,7 @@ impl<T> Key<T> {
F: FnOnce() -> R,
{
struct Reset<'a, T: 'a> {
key: &'a KeyInner<T>,
key: &'a __impl::KeyInner<T>,
val: *mut T,
}
#[unsafe_destructor]
Expand Down