//! `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
/// 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
// 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)]
/// 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;
(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;
// 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.
///
/// 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)();
}
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"))]
use prelude::*;
use cell::UnsafeCell;
+ use super::State;
use thread::Thread;
struct Foo(Sender<()>);
});
}
+ #[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 {
fn drop(&mut self) {
unsafe {
HITS += 1;
- if K2.destroyed() {
+ if K2.state() == State::Destroyed {
assert_eq!(HITS, 3);
} else {
if HITS == 1 {
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));
}
impl Drop for S1 {
fn drop(&mut self) {
- assert!(K1.destroyed());
+ assert!(K1.state() == State::Destroyed);
}
}
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())));
}
}
//! ```
#![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`.
/// 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.
///
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
})
}
F: FnOnce() -> R,
{
struct Reset<'a, T: 'a> {
- key: &'a KeyInner<T>,
+ key: &'a __impl::KeyInner<T>,
val: *mut T,
}
#[unsafe_destructor]