diff options
Diffstat (limited to 'src/libstd/thread_local')
| -rw-r--r-- | src/libstd/thread_local/mod.rs | 762 | ||||
| -rw-r--r-- | src/libstd/thread_local/scoped.rs | 313 |
2 files changed, 0 insertions, 1075 deletions
diff --git a/src/libstd/thread_local/mod.rs b/src/libstd/thread_local/mod.rs deleted file mode 100644 index 08780292c88..00000000000 --- a/src/libstd/thread_local/mod.rs +++ /dev/null @@ -1,762 +0,0 @@ -// Copyright 2014-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. - -//! Thread local storage -//! -//! This module provides an implementation of thread local storage for Rust -//! programs. Thread local storage is a method of storing data into a global -//! variable which each thread in the program will have its own copy of. -//! Threads do not share this data, so accesses do not need to be synchronized. -//! -//! At a high level, this module provides two variants of storage: -//! -//! * Owning thread local storage. This is a type of thread local key which -//! owns the value that it contains, and will destroy the value when the -//! thread exits. This variant is created with the `thread_local!` macro and -//! can contain any value which is `'static` (no borrowed pointers. -//! -//! * Scoped thread local storage. This type of key is used to store a reference -//! to a value into local storage temporarily for the scope of a function -//! call. There are no restrictions on what types of values can be placed -//! into this key. -//! -//! Both forms of thread local storage provide an accessor function, `with`, -//! which will yield a shared reference to the value to the specified -//! closure. Thread local keys only allow shared access to values as there is no -//! way to guarantee uniqueness if a mutable borrow was allowed. Most values -//! will want to make use of some form of **interior mutability** through the -//! `Cell` or `RefCell` types. - -#![stable(feature = "rust1", since = "1.0.0")] - -use prelude::v1::*; - -use cell::UnsafeCell; - -#[macro_use] -pub mod scoped; - -// Sure wish we had macro hygiene, no? -#[doc(hidden)] -#[unstable(feature = "thread_local_internals")] -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 -/// target platform. It is instantiated with the `thread_local!` macro and the -/// primary method is the `with` method. -/// -/// The `with` method yields a reference to the contained value which cannot be -/// sent across tasks or escape the given closure. -/// -/// # Initialization and Destruction -/// -/// Initialization is dynamically performed on the first call to `with()` -/// within a thread, and values support destructors which will be run when a -/// thread exits. -/// -/// # Examples -/// -/// ``` -/// use std::cell::RefCell; -/// use std::thread; -/// -/// thread_local!(static FOO: RefCell<u32> = RefCell::new(1)); -/// -/// FOO.with(|f| { -/// assert_eq!(*f.borrow(), 1); -/// *f.borrow_mut() = 2; -/// }); -/// -/// // each thread starts out with the initial value of 1 -/// thread::spawn(move|| { -/// FOO.with(|f| { -/// assert_eq!(*f.borrow(), 1); -/// *f.borrow_mut() = 3; -/// }); -/// }); -/// -/// // we retain our original value of 2 despite the child thread -/// FOO.with(|f| { -/// assert_eq!(*f.borrow(), 2); -/// }); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -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 - // address of another thread_local static. For this reason we kinda wonkily - // work around this by generating a shim function which will give us the - // address of the inner TLS key at runtime. - // - // 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)] - #[unstable(feature = "thread_local_internals")] - pub inner: fn() -> &'static __impl::KeyInner<UnsafeCell<Option<T>>>, - - // initialization routine to invoke to create a value - #[doc(hidden)] - #[unstable(feature = "thread_local_internals")] - pub init: fn() -> T, -} - -/// Declare a new thread local storage key of type `std::thread_local::Key`. -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -#[allow_internal_unstable] -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::__impl::KeyInner as __KeyInner; - use std::option::Option as __Option; - use std::option::Option::None as __None; - - __thread_local_inner!(static __KEY: __UnsafeCell<__Option<$t>> = { - __UnsafeCell { value: __None } - }); - fn __init() -> $t { $init } - fn __getit() -> &'static __KeyInner<__UnsafeCell<__Option<$t>>> { - &__KEY - } - ::std::thread_local::Key { inner: __getit, init: __init } - }; - ); - (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::__impl::KeyInner as __KeyInner; - use std::option::Option as __Option; - use std::option::Option::None as __None; - - __thread_local_inner!(static __KEY: __UnsafeCell<__Option<$t>> = { - __UnsafeCell { value: __None } - }); - fn __init() -> $t { $init } - fn __getit() -> &'static __KeyInner<__UnsafeCell<__Option<$t>>> { - &__KEY - } - ::std::thread_local::Key { inner: __getit, init: __init } - }; - ); -} - -// Macro pain #4586: -// -// When cross compiling, rustc will load plugins and macros from the *host* -// platform before search for macros from the target platform. This is primarily -// done to detect, for example, plugins. Ideally the macro below would be -// defined once per module below, but unfortunately this means we have the -// following situation: -// -// 1. We compile libstd for x86_64-unknown-linux-gnu, this thread_local!() macro -// will inject #[thread_local] statics. -// 2. We then try to compile a program for arm-linux-androideabi -// 3. The compiler has a host of linux and a target of android, so it loads -// macros from the *linux* libstd. -// 4. The macro generates a #[thread_local] field, but the android libstd does -// not use #[thread_local] -// 5. Compile error about structs with wrong fields. -// -// To get around this, we're forced to inject the #[cfg] logic into the macro -// itself. Woohoo. - -#[macro_export] -#[doc(hidden)] -#[allow_internal_unstable] -macro_rules! __thread_local_inner { - (static $name:ident: $t:ty = $init:expr) => ( - #[cfg_attr(all(any(target_os = "macos", target_os = "linux"), - not(target_arch = "aarch64")), - thread_local)] - static $name: ::std::thread_local::__impl::KeyInner<$t> = - __thread_local_inner!($init, $t); - ); - (pub static $name:ident: $t:ty = $init:expr) => ( - #[cfg_attr(all(any(target_os = "macos", target_os = "linux"), - not(target_arch = "aarch64")), - thread_local)] - pub static $name: ::std::thread_local::__impl::KeyInner<$t> = - __thread_local_inner!($init, $t); - ); - ($init:expr, $t:ty) => ({ - #[cfg(all(any(target_os = "macos", target_os = "linux"), not(target_arch = "aarch64")))] - 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(any(not(any(target_os = "macos", target_os = "linux")), target_arch = "aarch64"))] - const _INIT: ::std::thread_local::__impl::KeyInner<$t> = { - unsafe extern fn __destroy(ptr: *mut u8) { - ::std::thread_local::__impl::destroy_value::<$t>(ptr); - } - - ::std::thread_local::__impl::KeyInner { - inner: ::std::cell::UnsafeCell { value: $init }, - 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 - }); -} - -/// Indicator of the state of a thread local storage key. -#[unstable(feature = "std_misc", - reason = "state querying was recently added")] -#[derive(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 will lazily initialize the value if this thread has not referenced - /// this key yet. - /// - /// # Panics - /// - /// 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(feature = "rust1", since = "1.0.0")] - pub fn with<F, R>(&'static self, f: F) -> R - where F: FnOnce(&T) -> R { - let slot = (self.inner)(); - unsafe { - let slot = slot.get().expect("cannot access a TLS value during or \ - after it is destroyed"); - f(match *slot.get() { - Some(ref inner) => inner, - None => self.init(slot), - }) - } - } - - unsafe fn init(&self, slot: &UnsafeCell<Option<T>>) -> &T { - // 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() - } - - /// 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. - /// - /// 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(feature = "std_misc", - reason = "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 - #[unstable(feature = "std_misc")] - #[deprecated(since = "1.0.0", - reason = "function renamed to state() and returns more info")] - pub fn destroyed(&'static self) -> bool { self.state() == State::Destroyed } -} - -#[cfg(all(any(target_os = "macos", target_os = "linux"), not(target_arch = "aarch64")))] -mod imp { - use prelude::v1::*; - - use cell::UnsafeCell; - use intrinsics; - use ptr; - - #[doc(hidden)] - #[unstable(feature = "thread_local_internals")] - pub struct Key<T> { - // Place the inner bits in an `UnsafeCell` to currently get around the - // "only Sync statics" restriction. This allows any type to be placed in - // the cell. - // - // Note that all access requires `T: 'static` so it can't be a type with - // any borrowed pointers still. - #[unstable(feature = "thread_local_internals")] - pub inner: UnsafeCell<T>, - - // Metadata to keep track of the state of the destructor. Remember that - // these variables are thread-local, not global. - #[unstable(feature = "thread_local_internals")] - pub dtor_registered: UnsafeCell<bool>, // should be Cell - #[unstable(feature = "thread_local_internals")] - pub dtor_running: UnsafeCell<bool>, // should be Cell - } - - unsafe impl<T> ::marker::Sync for Key<T> { } - - #[doc(hidden)] - impl<T> Key<T> { - pub unsafe fn get(&'static self) -> Option<&'static T> { - if intrinsics::needs_drop::<T>() && *self.dtor_running.get() { - return None - } - self.register_dtor(); - Some(&*self.inner.get()) - } - - unsafe fn register_dtor(&self) { - if !intrinsics::needs_drop::<T>() || *self.dtor_registered.get() { - return - } - - register_dtor(self as *const _ as *mut u8, - destroy_value::<T>); - *self.dtor_registered.get() = true; - } - } - - // Since what appears to be glibc 2.18 this symbol has been shipped which - // GCC and clang both use to invoke destructors in thread_local globals, so - // let's do the same! - // - // Note, however, that we run on lots older linuxes, as well as cross - // compiling from a newer linux to an older linux, so we also have a - // fallback implementation to use as well. - // - // Due to rust-lang/rust#18804, make sure this is not generic! - #[cfg(target_os = "linux")] - unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { - use boxed; - use mem; - use libc; - use sys_common::thread_local as os; - - extern { - static __dso_handle: *mut u8; - #[linkage = "extern_weak"] - static __cxa_thread_atexit_impl: *const (); - } - if !__cxa_thread_atexit_impl.is_null() { - type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8), - arg: *mut u8, - dso_handle: *mut u8) -> libc::c_int; - mem::transmute::<*const (), F>(__cxa_thread_atexit_impl) - (dtor, t, __dso_handle); - return - } - - // The fallback implementation uses a vanilla OS-based TLS key to track - // the list of destructors that need to be run for this thread. The key - // then has its own destructor which runs all the other destructors. - // - // The destructor for DTORS is a little special in that it has a `while` - // loop to continuously drain the list of registered destructors. It - // *should* be the case that this loop always terminates because we - // provide the guarantee that a TLS key cannot be set after it is - // flagged for destruction. - static DTORS: os::StaticKey = os::StaticKey { - inner: os::INIT_INNER, - dtor: Some(run_dtors as unsafe extern "C" fn(*mut u8)), - }; - type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>; - if DTORS.get().is_null() { - let v: Box<List> = box Vec::new(); - DTORS.set(boxed::into_raw(v) as *mut u8); - } - let list: &mut List = &mut *(DTORS.get() as *mut List); - list.push((t, dtor)); - - unsafe extern fn run_dtors(mut ptr: *mut u8) { - while !ptr.is_null() { - let list: Box<List> = Box::from_raw(ptr as *mut List); - for &(ptr, dtor) in &*list { - dtor(ptr); - } - ptr = DTORS.get(); - DTORS.set(ptr::null_mut()); - } - } - } - - // OSX's analog of the above linux function is this _tlv_atexit function. - // The disassembly of thread_local globals in C++ (at least produced by - // clang) will have this show up in the output. - #[cfg(target_os = "macos")] - unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { - extern { - fn _tlv_atexit(dtor: unsafe extern fn(*mut u8), - arg: *mut u8); - } - _tlv_atexit(dtor, t); - } - - #[doc(hidden)] - #[unstable(feature = "thread_local_internals")] - pub unsafe extern fn destroy_value<T>(ptr: *mut u8) { - let ptr = ptr as *mut Key<T>; - // Right before we run the user destructor be sure to flag the - // destructor as running for this thread so calls to `get` will return - // `None`. - *(*ptr).dtor_running.get() = true; - ptr::read((*ptr).inner.get()); - } -} - -#[cfg(any(not(any(target_os = "macos", target_os = "linux")), target_arch = "aarch64"))] -mod imp { - use prelude::v1::*; - - use alloc::boxed; - use cell::UnsafeCell; - use mem; - use ptr; - use sys_common::thread_local::StaticKey as OsStaticKey; - - #[doc(hidden)] - #[unstable(feature = "thread_local_internals")] - pub struct Key<T> { - // Statically allocated initialization expression, using an `UnsafeCell` - // for the same reasons as above. - #[unstable(feature = "thread_local_internals")] - pub inner: UnsafeCell<T>, - - // OS-TLS key that we'll use to key off. - #[unstable(feature = "thread_local_internals")] - pub os: OsStaticKey, - } - - unsafe impl<T> ::marker::Sync for Key<T> { } - - struct Value<T: 'static> { - key: &'static Key<T>, - value: T, - } - - #[doc(hidden)] - impl<T> Key<T> { - pub unsafe fn get(&'static self) -> Option<&'static T> { - self.ptr().map(|p| &*p) - } - - unsafe fn ptr(&'static self) -> Option<*mut T> { - let ptr = self.os.get() as *mut Value<T>; - if !ptr.is_null() { - if ptr as usize == 1 { - return None - } - return Some(&mut (*ptr).value as *mut T); - } - - // If the lookup returned null, we haven't initialized our own local - // copy, so do that now. - // - // Also note that this transmute_copy should be ok because the value - // `inner` is already validated to be a valid `static` value, so we - // should be able to freely copy the bits. - let ptr: Box<Value<T>> = box Value { - key: self, - value: mem::transmute_copy(&self.inner), - }; - let ptr: *mut Value<T> = boxed::into_raw(ptr); - self.os.set(ptr as *mut u8); - Some(&mut (*ptr).value as *mut T) - } - } - - #[doc(hidden)] - #[unstable(feature = "thread_local_internals")] - pub unsafe extern fn destroy_value<T: 'static>(ptr: *mut u8) { - // The OS TLS ensures that this key contains a NULL value when this - // destructor starts to run. We set it back to a sentinel value of 1 to - // ensure that any future calls to `get` for this thread will return - // `None`. - // - // Note that to prevent an infinite loop we reset it back to null right - // before we return from the destructor ourselves. - let ptr: Box<Value<T>> = Box::from_raw(ptr as *mut Value<T>); - let key = ptr.key; - key.os.set(1 as *mut u8); - drop(ptr); - key.os.set(ptr::null_mut()); - } -} - -#[cfg(test)] -mod tests { - use prelude::v1::*; - - use sync::mpsc::{channel, Sender}; - use cell::UnsafeCell; - use super::State; - use thread; - - struct Foo(Sender<()>); - - impl Drop for Foo { - fn drop(&mut self) { - let Foo(ref s) = *self; - s.send(()).unwrap(); - } - } - - #[test] - fn smoke_no_dtor() { - thread_local!(static FOO: UnsafeCell<i32> = UnsafeCell { value: 1 }); - - FOO.with(|f| unsafe { - assert_eq!(*f.get(), 1); - *f.get() = 2; - }); - let (tx, rx) = channel(); - let _t = thread::spawn(move|| { - FOO.with(|f| unsafe { - assert_eq!(*f.get(), 1); - }); - tx.send(()).unwrap(); - }); - rx.recv().unwrap(); - - FOO.with(|f| unsafe { - assert_eq!(*f.get(), 2); - }); - } - - #[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 { - value: None - }); - - let (tx, rx) = channel(); - let _t = thread::spawn(move|| unsafe { - let mut tx = Some(tx); - FOO.with(|f| { - *f.get() = Some(Foo(tx.take().unwrap())); - }); - }); - rx.recv().unwrap(); - } - - #[test] - fn circular() { - struct S1; - struct S2; - thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell { - value: None - }); - thread_local!(static K2: UnsafeCell<Option<S2>> = UnsafeCell { - value: None - }); - static mut HITS: u32 = 0; - - impl Drop for S1 { - fn drop(&mut self) { - unsafe { - HITS += 1; - if K2.state() == State::Destroyed { - assert_eq!(HITS, 3); - } else { - if HITS == 1 { - K2.with(|s| *s.get() = Some(S2)); - } else { - assert_eq!(HITS, 3); - } - } - } - } - } - impl Drop for S2 { - fn drop(&mut self) { - unsafe { - HITS += 1; - assert!(K1.state() != State::Destroyed); - assert_eq!(HITS, 2); - K1.with(|s| *s.get() = Some(S1)); - } - } - } - - thread::spawn(move|| { - drop(S1); - }).join().ok().unwrap(); - } - - #[test] - fn self_referential() { - struct S1; - thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell { - value: None - }); - - impl Drop for S1 { - fn drop(&mut self) { - assert!(K1.state() == State::Destroyed); - } - } - - thread::spawn(move|| unsafe { - K1.with(|s| *s.get() = Some(S1)); - }).join().ok().unwrap(); - } - - #[test] - fn dtors_in_dtors_in_dtors() { - struct S1(Sender<()>); - thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell { - value: None - }); - thread_local!(static K2: UnsafeCell<Option<Foo>> = UnsafeCell { - value: None - }); - - impl Drop for S1 { - fn drop(&mut self) { - let S1(ref tx) = *self; - unsafe { - if K2.state() != State::Destroyed { - K2.with(|s| *s.get() = Some(Foo(tx.clone()))); - } - } - } - } - - let (tx, rx) = channel(); - let _t = thread::spawn(move|| unsafe { - let mut tx = Some(tx); - K1.with(|s| *s.get() = Some(S1(tx.take().unwrap()))); - }); - rx.recv().unwrap(); - } -} - -#[cfg(test)] -mod dynamic_tests { - use prelude::v1::*; - - use cell::RefCell; - use collections::HashMap; - - #[test] - fn smoke() { - fn square(i: i32) -> i32 { i * i } - thread_local!(static FOO: i32 = square(3)); - - FOO.with(|f| { - assert_eq!(*f, 9); - }); - } - - #[test] - fn hashmap() { - fn map() -> RefCell<HashMap<i32, i32>> { - let mut m = HashMap::new(); - m.insert(1, 2); - RefCell::new(m) - } - thread_local!(static FOO: RefCell<HashMap<i32, i32>> = map()); - - FOO.with(|map| { - assert_eq!(map.borrow()[1], 2); - }); - } - - #[test] - fn refcell_vec() { - thread_local!(static FOO: RefCell<Vec<u32>> = RefCell::new(vec![1, 2, 3])); - - FOO.with(|vec| { - assert_eq!(vec.borrow().len(), 3); - vec.borrow_mut().push(4); - assert_eq!(vec.borrow()[3], 4); - }); - } -} diff --git a/src/libstd/thread_local/scoped.rs b/src/libstd/thread_local/scoped.rs deleted file mode 100644 index 86e6c059a70..00000000000 --- a/src/libstd/thread_local/scoped.rs +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright 2014-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. - -//! Scoped thread-local storage -//! -//! This module provides the ability to generate *scoped* thread-local -//! variables. In this sense, scoped indicates that thread local storage -//! actually stores a reference to a value, and this reference is only placed -//! in storage for a scoped amount of time. -//! -//! There are no restrictions on what types can be placed into a scoped -//! variable, but all scoped variables are initialized to the equivalent of -//! null. Scoped thread local storage is useful when a value is present for a known -//! period of time and it is not required to relinquish ownership of the -//! contents. -//! -//! # Examples -//! -//! ``` -//! scoped_thread_local!(static FOO: u32); -//! -//! // Initially each scoped slot is empty. -//! assert!(!FOO.is_set()); -//! -//! // When inserting a value, the value is only in place for the duration -//! // of the closure specified. -//! FOO.set(&1, || { -//! FOO.with(|slot| { -//! assert_eq!(*slot, 1); -//! }); -//! }); -//! ``` - -#![unstable(feature = "std_misc", - reason = "scoped TLS has yet to have wide enough use to fully consider \ - stabilizing its interface")] - -use prelude::v1::*; - -// macro hygiene sure would be nice, wouldn't it? -#[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`. -/// -/// Keys are statically allocated and can contain a reference to an instance of -/// 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: __impl::KeyInner<T> } - -/// Declare a new scoped thread local storage key. -/// -/// This macro declares a `static` item on which methods are used to get and -/// set the value stored within. -#[macro_export] -#[allow_internal_unstable] -macro_rules! scoped_thread_local { - (static $name:ident: $t:ty) => ( - __scoped_thread_local_inner!(static $name: $t); - ); - (pub static $name:ident: $t:ty) => ( - __scoped_thread_local_inner!(pub static $name: $t); - ); -} - -#[macro_export] -#[doc(hidden)] -#[allow_internal_unstable] -macro_rules! __scoped_thread_local_inner { - (static $name:ident: $t:ty) => ( - #[cfg_attr(not(any(windows, - target_os = "android", - target_os = "ios", - target_os = "openbsd", - target_arch = "aarch64")), - thread_local)] - static $name: ::std::thread_local::scoped::Key<$t> = - __scoped_thread_local_inner!($t); - ); - (pub static $name:ident: $t:ty) => ( - #[cfg_attr(not(any(windows, - target_os = "android", - target_os = "ios", - target_os = "openbsd", - target_arch = "aarch64")), - thread_local)] - pub static $name: ::std::thread_local::scoped::Key<$t> = - __scoped_thread_local_inner!($t); - ); - ($t:ty) => ({ - use std::thread_local::scoped::Key as __Key; - - #[cfg(not(any(windows, - target_os = "android", - target_os = "ios", - target_os = "openbsd", - target_arch = "aarch64")))] - 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", - target_os = "openbsd", - target_arch = "aarch64"))] - const _INIT: __Key<$t> = __Key { - inner: ::std::thread_local::scoped::__impl::KeyInner { - inner: ::std::thread_local::scoped::__impl::OS_INIT, - marker: ::std::marker::PhantomData::<::std::cell::Cell<$t>>, - } - }; - - _INIT - }) -} - -impl<T> Key<T> { - /// Insert a value into this scoped thread local storage slot for a - /// duration of a closure. - /// - /// While `cb` is running, the value `t` will be returned by `get` unless - /// this function is called recursively inside of `cb`. - /// - /// Upon return, this function will restore the previous value, if any - /// was available. - /// - /// # Examples - /// - /// ``` - /// scoped_thread_local!(static FOO: u32); - /// - /// FOO.set(&100, || { - /// let val = FOO.with(|v| *v); - /// assert_eq!(val, 100); - /// - /// // set can be called recursively - /// FOO.set(&101, || { - /// // ... - /// }); - /// - /// // Recursive calls restore the previous value. - /// let val = FOO.with(|v| *v); - /// assert_eq!(val, 100); - /// }); - /// ``` - pub fn set<R, F>(&'static self, t: &T, cb: F) -> R where - F: FnOnce() -> R, - { - struct Reset<'a, T: 'a> { - key: &'a __impl::KeyInner<T>, - val: *mut T, - } - #[unsafe_destructor] - impl<'a, T> Drop for Reset<'a, T> { - fn drop(&mut self) { - unsafe { self.key.set(self.val) } - } - } - - let prev = unsafe { - let prev = self.inner.get(); - self.inner.set(t as *const T as *mut T); - prev - }; - - let _reset = Reset { key: &self.inner, val: prev }; - cb() - } - - /// Get a value out of this scoped variable. - /// - /// This function takes a closure which receives the value of this - /// variable. - /// - /// # Panics - /// - /// This function will panic if `set` has not previously been called. - /// - /// # Examples - /// - /// ```no_run - /// scoped_thread_local!(static FOO: u32); - /// - /// FOO.with(|slot| { - /// // work with `slot` - /// }); - /// ``` - pub fn with<R, F>(&'static self, cb: F) -> R where - F: FnOnce(&T) -> R - { - unsafe { - let ptr = self.inner.get(); - assert!(!ptr.is_null(), "cannot access a scoped thread local \ - variable without calling `set` first"); - cb(&*ptr) - } - } - - /// Test whether this TLS key has been `set` for the current thread. - pub fn is_set(&'static self) -> bool { - unsafe { !self.inner.get().is_null() } - } -} - -#[cfg(not(any(windows, - target_os = "android", - target_os = "ios", - target_os = "openbsd", - target_arch = "aarch64")))] -mod imp { - use std::cell::UnsafeCell; - - #[doc(hidden)] - pub struct KeyInner<T> { pub inner: UnsafeCell<*mut T> } - - unsafe impl<T> ::marker::Sync for KeyInner<T> { } - - #[doc(hidden)] - impl<T> KeyInner<T> { - #[doc(hidden)] - pub unsafe fn set(&self, ptr: *mut T) { *self.inner.get() = ptr; } - #[doc(hidden)] - pub unsafe fn get(&self) -> *mut T { *self.inner.get() } - } -} - -#[cfg(any(windows, - target_os = "android", - target_os = "ios", - target_os = "openbsd", - target_arch = "aarch64"))] -mod imp { - use marker; - use std::cell::Cell; - use sys_common::thread_local::StaticKey as OsStaticKey; - - #[doc(hidden)] - pub struct KeyInner<T> { - pub inner: OsStaticKey, - pub marker: marker::PhantomData<Cell<T>>, - } - - unsafe impl<T> ::marker::Sync for KeyInner<T> { } - - #[doc(hidden)] - impl<T> KeyInner<T> { - #[doc(hidden)] - pub unsafe fn set(&self, ptr: *mut T) { self.inner.set(ptr as *mut _) } - #[doc(hidden)] - pub unsafe fn get(&self) -> *mut T { self.inner.get() as *mut _ } - } -} - - -#[cfg(test)] -mod tests { - use cell::Cell; - use prelude::v1::*; - - scoped_thread_local!(static FOO: u32); - - #[test] - fn smoke() { - scoped_thread_local!(static BAR: u32); - - assert!(!BAR.is_set()); - BAR.set(&1, || { - assert!(BAR.is_set()); - BAR.with(|slot| { - assert_eq!(*slot, 1); - }); - }); - assert!(!BAR.is_set()); - } - - #[test] - fn cell_allowed() { - scoped_thread_local!(static BAR: Cell<u32>); - - BAR.set(&Cell::new(1), || { - BAR.with(|slot| { - assert_eq!(slot.get(), 1); - }); - }); - } - - #[test] - fn scope_item_allowed() { - assert!(!FOO.is_set()); - FOO.set(&1, || { - assert!(FOO.is_set()); - FOO.with(|slot| { - assert_eq!(*slot, 1); - }); - }); - assert!(!FOO.is_set()); - } -} |
