diff options
| -rw-r--r-- | src/libstd/sys_common/thread_info.rs | 11 | ||||
| -rw-r--r-- | src/libstd/thread/local.rs | 63 | ||||
| -rw-r--r-- | src/libstd/thread/mod.rs | 2 | ||||
| -rw-r--r-- | src/test/run-pass/tls-try-with.rs | 37 |
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 }); +} |
