about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libstd/sys_common/thread_info.rs11
-rw-r--r--src/libstd/thread/local.rs63
-rw-r--r--src/libstd/thread/mod.rs2
-rw-r--r--src/test/run-pass/tls-try-with.rs37
4 files changed, 95 insertions, 18 deletions
diff --git a/src/libstd/sys_common/thread_info.rs b/src/libstd/sys_common/thread_info.rs
index 5ed48ee4558..2abb8afa828 100644
--- a/src/libstd/sys_common/thread_info.rs
+++ b/src/libstd/sys_common/thread_info.rs
@@ -12,7 +12,6 @@
 
 use cell::RefCell;
 use thread::Thread;
-use thread::LocalKeyState;
 
 struct ThreadInfo {
     stack_guard: Option<usize>,
@@ -23,19 +22,15 @@ thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = RefCell::new(N
 
 impl ThreadInfo {
     fn with<R, F>(f: F) -> Option<R> where F: FnOnce(&mut ThreadInfo) -> R {
-        if THREAD_INFO.state() == LocalKeyState::Destroyed {
-            return None
-        }
-
-        THREAD_INFO.with(move |c| {
+        THREAD_INFO.try_with(move |c| {
             if c.borrow().is_none() {
                 *c.borrow_mut() = Some(ThreadInfo {
                     stack_guard: None,
                     thread: Thread::new(None),
                 })
             }
-            Some(f(c.borrow_mut().as_mut().unwrap()))
-        })
+            f(c.borrow_mut().as_mut().unwrap())
+        }).ok()
     }
 }
 
diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs
index dad21473eae..28e8f72ac64 100644
--- a/src/libstd/thread/local.rs
+++ b/src/libstd/thread/local.rs
@@ -232,6 +232,32 @@ pub enum LocalKeyState {
     Destroyed,
 }
 
+/// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with).
+#[unstable(feature = "thread_local_state",
+           reason = "state querying was recently added",
+           issue = "27716")]
+pub struct AccessError {
+    _private: (),
+}
+
+#[unstable(feature = "thread_local_state",
+           reason = "state querying was recently added",
+           issue = "27716")]
+impl fmt::Debug for AccessError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("AccessError").finish()
+    }
+}
+
+#[unstable(feature = "thread_local_state",
+           reason = "state querying was recently added",
+           issue = "27716")]
+impl fmt::Display for AccessError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Display::fmt("already destroyed", f)
+    }
+}
+
 impl<T: 'static> LocalKey<T> {
     #[doc(hidden)]
     #[unstable(feature = "thread_local_internals",
@@ -258,15 +284,8 @@ impl<T: 'static> LocalKey<T> {
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn with<F, R>(&'static self, f: F) -> R
                       where F: FnOnce(&T) -> R {
-        unsafe {
-            let slot = (self.inner)();
-            let slot = slot.expect("cannot access a TLS value during or \
-                                    after it is destroyed");
-            f(match *slot.get() {
-                Some(ref inner) => inner,
-                None => self.init(slot),
-            })
-        }
+        self.try_with(f).expect("cannot access a TLS value during or \
+                                 after it is destroyed")
     }
 
     unsafe fn init(&self, slot: &UnsafeCell<Option<T>>) -> &T {
@@ -331,6 +350,32 @@ impl<T: 'static> LocalKey<T> {
             }
         }
     }
+
+    /// Acquires a reference to the value in this TLS key.
+    ///
+    /// This will lazily initialize the value if this thread has not referenced
+    /// this key yet. If the key has been destroyed (which may happen if this is called
+    /// in a destructor), this function will return a ThreadLocalError.
+    ///
+    /// # Panics
+    ///
+    /// This function will still `panic!()` if the key is uninitialized and the
+    /// key's initializer panics.
+    #[unstable(feature = "thread_local_state",
+               reason = "state querying was recently added",
+               issue = "27716")]
+    pub fn try_with<F, R>(&'static self, f: F) -> Result<R, AccessError>
+                      where F: FnOnce(&T) -> R {
+        unsafe {
+            let slot = (self.inner)().ok_or(AccessError {
+                _private: (),
+            })?;
+            Ok(f(match *slot.get() {
+                Some(ref inner) => inner,
+                None => self.init(slot),
+            }))
+        }
+    }
 }
 
 #[doc(hidden)]
diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs
index f4fe52ca3b3..07a3a01ce86 100644
--- a/src/libstd/thread/mod.rs
+++ b/src/libstd/thread/mod.rs
@@ -159,7 +159,7 @@ use time::Duration;
 #[macro_use] mod local;
 
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use self::local::{LocalKey, LocalKeyState};
+pub use self::local::{LocalKey, LocalKeyState, AccessError};
 
 // The types used by the thread_local! macro to access TLS keys. Note that there
 // are two types, the "OS" type and the "fast" type. The OS thread local key
diff --git a/src/test/run-pass/tls-try-with.rs b/src/test/run-pass/tls-try-with.rs
new file mode 100644
index 00000000000..c072ec0679d
--- /dev/null
+++ b/src/test/run-pass/tls-try-with.rs
@@ -0,0 +1,37 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-emscripten no threads support
+
+#![feature(thread_local_state)]
+
+use std::thread;
+
+static mut DROP_RUN: bool = false;
+
+struct Foo;
+
+thread_local!(static FOO: Foo = Foo {});
+
+impl Drop for Foo {
+    fn drop(&mut self) {
+        assert!(FOO.try_with(|_| panic!("`try_with` closure run")).is_err());
+        unsafe { DROP_RUN = true; }
+    }
+}
+
+fn main() {
+    thread::spawn(|| {
+        assert_eq!(FOO.try_with(|_| {
+            132
+        }).expect("`try_with` failed"), 132);
+    }).join().unwrap();
+    assert!(unsafe { DROP_RUN });
+}