Skip to content

Commit

Permalink
Auto merge of #43158 - PlasmaPower:thread-local-try-with, r=alexcrichton
Browse files Browse the repository at this point in the history
Thread local try with

rust-lang/rfcs#2030 was turned into this PR (the RFC was closed, but it looks like just a PR should be good).

See also: state stabilization issue: #27716

`try_with` is used in two places in std: stdio and thread_info. In stdio, it would be better if the result was passed to the closure, but in thread_info, it's better as is where the result is returned from the function call. I'm not sure which is better, but I prefer the current way as it better represents the scope.
  • Loading branch information
bors committed Jul 13, 2017
2 parents 06ffdeb + a301f84 commit b2c0707
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 18 deletions.
11 changes: 3 additions & 8 deletions src/libstd/sys_common/thread_info.rs
Expand Up @@ -12,7 +12,6 @@

use cell::RefCell;
use thread::Thread;
use thread::LocalKeyState;

struct ThreadInfo {
stack_guard: Option<usize>,
Expand All @@ -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()
}
}

Expand Down
63 changes: 54 additions & 9 deletions src/libstd/thread/local.rs
Expand Up @@ -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",
Expand All @@ -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 {
Expand Down Expand Up @@ -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)]
Expand Down
2 changes: 1 addition & 1 deletion src/libstd/thread/mod.rs
Expand Up @@ -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
Expand Down
37 changes: 37 additions & 0 deletions 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 });
}

0 comments on commit b2c0707

Please sign in to comment.