]> git.lizzy.rs Git - rust.git/commitdiff
std: Second pass stabilization for thread_local
authorAlex Crichton <alex@alexcrichton.com>
Tue, 30 Dec 2014 23:20:47 +0000 (15:20 -0800)
committerAlex Crichton <alex@alexcrichton.com>
Wed, 31 Dec 2014 23:50:28 +0000 (15:50 -0800)
This commit performs a second pass over the `std::thread_local` module. Most of
the functionality remains explicitly unstable, but the specific actions taken
were:

* `thread_local` is now stable
* `thread_local!` is now stable
* `thread_local::Key` is now stable
* `thread_local::Key::with` is now stable
* `thread_local::Key::destroyed` is deprecated in favor of a more general
  `state` function
* `thread_local::Key::state` was added to query the three states that a key can
  be in: uninitialized, valid, or destroyed. This function, and the
  corresponding `State` enum, are both marked unstable as we may wish to expand
  it later on.
* `thread_local::scoped` is entirely unstable. There hasn't been a whole lot of
  usage of this module in the standard distribution, so it remains unstable at
  this time.

Note that while the structure `Key` is marked stable, it is currently forced to
expose all of its implementation details due to the use of
construction-via-macro. The use of construction-via-macro is currently required
in order to place the `#[thread_local]` attribute on static in a
platform-specific manner. These stability attributes were assigned assuming that
it will be acceptable to tweak the implementation of `Key` in the future.

src/libstd/sys/common/thread_info.rs
src/libstd/thread_local/mod.rs
src/libstd/thread_local/scoped.rs

index dc21feb17a8e83c660788c6d720399debf17cb8c..b8bff23d5833883c5725d7620d5b2d74e180928a 100644 (file)
 
 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 @@ struct ThreadInfo {
 
 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");
         }
index 14dd2a1ac9b587208c3426338331c8c498808f72..e317163f01c73ec440c58ee74364f89170326131 100644 (file)
 //! `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
@@ -90,6 +93,7 @@
 ///     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 @@ fn __getit() -> &'static __KeyInner<__UnsafeCell<__Option<$t>>> {
     (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 @@ fn __getit() -> &'static __KeyInner<__UnsafeCell<__Option<$t>>> {
 // 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 @@ pub fn with<F, R>(&'static self, f: F) -> R
     }
 
     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"))]
@@ -456,6 +527,7 @@ mod tests {
     use prelude::*;
 
     use cell::UnsafeCell;
+    use super::State;
     use thread::Thread;
 
     struct Foo(Sender<()>);
@@ -489,6 +561,29 @@ fn smoke_no_dtor() {
         });
     }
 
+    #[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 {
@@ -521,7 +616,7 @@ impl Drop for S1 {
             fn drop(&mut self) {
                 unsafe {
                     HITS += 1;
-                    if K2.destroyed() {
+                    if K2.state() == State::Destroyed {
                         assert_eq!(HITS, 3);
                     } else {
                         if HITS == 1 {
@@ -537,7 +632,7 @@ impl Drop for S2 {
             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));
                 }
@@ -558,7 +653,7 @@ fn self_referential() {
 
         impl Drop for S1 {
             fn drop(&mut self) {
-                assert!(K1.destroyed());
+                assert!(K1.state() == State::Destroyed);
             }
         }
 
@@ -581,7 +676,7 @@ impl Drop for S1 {
             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())));
                     }
                 }
index 5f96548c0530ac63badbeef3a827a5ddbc702b07..a5ad71ce6005875c83e45a83862acbe3563829eb 100644 (file)
 //! ```
 
 #![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`.
@@ -53,7 +58,7 @@
 /// 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 @@ pub fn set<R, F>(&'static self, t: &T, cb: F) -> R where
         F: FnOnce() -> R,
     {
         struct Reset<'a, T: 'a> {
-            key: &'a KeyInner<T>,
+            key: &'a __impl::KeyInner<T>,
             val: *mut T,
         }
         #[unsafe_destructor]