rollup merge of #20354: alexcrichton/second-pass-thread_local

Conflicts:
	src/libstd/sys/common/thread_info.rs
This commit is contained in:
Alex Crichton 2015-01-02 09:19:45 -08:00
commit 4b0e084aa1
3 changed files with 145 additions and 44 deletions

View File

@ -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)
@ -27,7 +28,7 @@ thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = RefCell::new(N
impl ThreadInfo {
fn with<R, F>(f: F) -> R where F: FnOnce(&mut ThreadInfo) -> 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");
}

View File

@ -35,20 +35,23 @@
//! `Cell` or `RefCell` types.
#![macro_escape]
#![experimental]
#![stable]
use prelude::v1::*;
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
@ -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
@ -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)]
@ -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;
@ -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;
@ -168,21 +172,22 @@ 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 },
@ -190,24 +195,54 @@ macro_rules! __thread_local_inner {
};
#[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.
///
@ -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)();
@ -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.
///
/// This will not initialize the key if it is not already initialized.
pub fn destroyed(&'static self) -> bool {
unsafe { (self.inner)().get().is_none() }
/// 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.
///
/// 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"))]
@ -457,6 +528,7 @@ mod tests {
use sync::mpsc::{channel, Sender};
use cell::UnsafeCell;
use super::State;
use thread::Thread;
struct Foo(Sender<()>);
@ -490,6 +562,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 {
@ -522,7 +617,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 {
@ -538,7 +633,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));
}
@ -559,7 +654,7 @@ mod tests {
impl Drop for S1 {
fn drop(&mut self) {
assert!(K1.destroyed());
assert!(K1.state() == State::Destroyed);
}
}
@ -582,7 +677,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())));
}
}

View File

@ -39,12 +39,17 @@
//! ```
#![macro_escape]
#![unstable = "scoped TLS has yet to have wide enough use to fully consider \
stabilizing its interface"]
use prelude::v1::*;
// 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`.
@ -53,7 +58,7 @@ use prelude::v1::*;
/// 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.
///
@ -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
})
}
@ -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]