diff --git a/src/libstd/sys/common/thread_info.rs b/src/libstd/sys/common/thread_info.rs index 8c76eb1504d..92b936e74f6 100644 --- a/src/libstd/sys/common/thread_info.rs +++ b/src/libstd/sys/common/thread_info.rs @@ -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> = RefCell::new(N impl ThreadInfo { fn with(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"); } diff --git a/src/libstd/thread_local/mod.rs b/src/libstd/thread_local/mod.rs index 4950337b9af..75e5ac59685 100644 --- a/src/libstd/thread_local/mod.rs +++ b/src/libstd/thread_local/mod.rs @@ -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 { // 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 { // 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>>, + pub inner: fn() -> &'static __impl::KeyInner>>, // initialization routine to invoke to create a value #[doc(hidden)] @@ -109,12 +113,12 @@ pub struct Key { /// 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 Key { /// Acquire a reference to the value in this TLS key. /// @@ -219,6 +254,7 @@ impl Key { /// 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(&'static self, f: F) -> R where F: FnOnce(&T) -> R { let slot = (self.inner)(); @@ -233,17 +269,52 @@ impl Key { } unsafe fn init(&self, slot: &UnsafeCell>) -> &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> = 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()))); } } diff --git a/src/libstd/thread_local/scoped.rs b/src/libstd/thread_local/scoped.rs index a2e2bd43849..c53d393f1eb 100644 --- a/src/libstd/thread_local/scoped.rs +++ b/src/libstd/thread_local/scoped.rs @@ -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 { #[doc(hidden)] pub inner: KeyInner } +pub struct Key { #[doc(hidden)] pub inner: __impl::KeyInner } /// 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 Key { F: FnOnce() -> R, { struct Reset<'a, T: 'a> { - key: &'a KeyInner, + key: &'a __impl::KeyInner, val: *mut T, } #[unsafe_destructor]