Fenrir
8 years ago
8 changed files with 1249 additions and 0 deletions
@ -0,0 +1,116 @@ |
|||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
#![cfg(target_thread_local)] |
||||||
|
#![unstable(feature = "thread_local_internals", issue = "0")] |
||||||
|
|
||||||
|
use cell::{Cell, UnsafeCell}; |
||||||
|
use intrinsics; |
||||||
|
use ptr; |
||||||
|
|
||||||
|
pub struct Key<T> { |
||||||
|
inner: UnsafeCell<Option<T>>, |
||||||
|
|
||||||
|
// Metadata to keep track of the state of the destructor. Remember that
|
||||||
|
// these variables are thread-local, not global.
|
||||||
|
dtor_registered: Cell<bool>, |
||||||
|
dtor_running: Cell<bool>, |
||||||
|
} |
||||||
|
|
||||||
|
unsafe impl<T> ::marker::Sync for Key<T> { } |
||||||
|
|
||||||
|
impl<T> Key<T> { |
||||||
|
pub const fn new() -> Key<T> { |
||||||
|
Key { |
||||||
|
inner: UnsafeCell::new(None), |
||||||
|
dtor_registered: Cell::new(false), |
||||||
|
dtor_running: Cell::new(false) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> { |
||||||
|
unsafe { |
||||||
|
if intrinsics::needs_drop::<T>() && self.dtor_running.get() { |
||||||
|
return None |
||||||
|
} |
||||||
|
self.register_dtor(); |
||||||
|
} |
||||||
|
Some(&self.inner) |
||||||
|
} |
||||||
|
|
||||||
|
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.set(true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { |
||||||
|
// 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.
|
||||||
|
use sys_common::thread_local as os; |
||||||
|
|
||||||
|
static DTORS: os::StaticKey = os::StaticKey::new(Some(run_dtors)); |
||||||
|
type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>; |
||||||
|
if DTORS.get().is_null() { |
||||||
|
let v: Box<List> = box Vec::new(); |
||||||
|
DTORS.set(Box::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.iter() { |
||||||
|
dtor(ptr); |
||||||
|
} |
||||||
|
ptr = DTORS.get(); |
||||||
|
DTORS.set(ptr::null_mut()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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.set(true); |
||||||
|
|
||||||
|
// The OSX implementation of TLS apparently had an odd aspect to it
|
||||||
|
// where the pointer we have may be overwritten while this destructor
|
||||||
|
// is running. Specifically if a TLS destructor re-accesses TLS it may
|
||||||
|
// trigger a re-initialization of all TLS variables, paving over at
|
||||||
|
// least some destroyed ones with initial values.
|
||||||
|
//
|
||||||
|
// This means that if we drop a TLS value in place on OSX that we could
|
||||||
|
// revert the value to its original state halfway through the
|
||||||
|
// destructor, which would be bad!
|
||||||
|
//
|
||||||
|
// Hence, we use `ptr::read` on OSX (to move to a "safe" location)
|
||||||
|
// instead of drop_in_place.
|
||||||
|
if cfg!(target_os = "macos") { |
||||||
|
ptr::read((*ptr).inner.get()); |
||||||
|
} else { |
||||||
|
ptr::drop_in_place((*ptr).inner.get()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
#![allow(dead_code)] // not used on all platforms
|
||||||
|
|
||||||
|
use collections::BTreeMap; |
||||||
|
use ptr; |
||||||
|
use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; |
||||||
|
|
||||||
|
pub type Key = usize; |
||||||
|
|
||||||
|
type Dtor = unsafe extern fn(*mut u8); |
||||||
|
|
||||||
|
static NEXT_KEY: AtomicUsize = ATOMIC_USIZE_INIT; |
||||||
|
|
||||||
|
static mut KEYS: *mut BTreeMap<Key, Option<Dtor>> = ptr::null_mut(); |
||||||
|
|
||||||
|
#[thread_local] |
||||||
|
static mut LOCALS: *mut BTreeMap<Key, *mut u8> = ptr::null_mut(); |
||||||
|
|
||||||
|
unsafe fn keys() -> &'static mut BTreeMap<Key, Option<Dtor>> { |
||||||
|
if KEYS == ptr::null_mut() { |
||||||
|
KEYS = Box::into_raw(Box::new(BTreeMap::new())); |
||||||
|
} |
||||||
|
&mut *KEYS |
||||||
|
} |
||||||
|
|
||||||
|
unsafe fn locals() -> &'static mut BTreeMap<Key, *mut u8> { |
||||||
|
if LOCALS == ptr::null_mut() { |
||||||
|
LOCALS = Box::into_raw(Box::new(BTreeMap::new())); |
||||||
|
} |
||||||
|
&mut *LOCALS |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
pub unsafe fn create(dtor: Option<Dtor>) -> Key { |
||||||
|
let key = NEXT_KEY.fetch_add(1, Ordering::SeqCst); |
||||||
|
keys().insert(key, dtor); |
||||||
|
key |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
pub unsafe fn get(key: Key) -> *mut u8 { |
||||||
|
if let Some(&entry) = locals().get(&key) { |
||||||
|
entry |
||||||
|
} else { |
||||||
|
ptr::null_mut() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
pub unsafe fn set(key: Key, value: *mut u8) { |
||||||
|
locals().insert(key, value); |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
pub unsafe fn destroy(key: Key) { |
||||||
|
keys().remove(&key); |
||||||
|
} |
@ -0,0 +1,270 @@ |
|||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
//! OS-based thread local storage
|
||||||
|
//!
|
||||||
|
//! This module provides an implementation of OS-based thread local storage,
|
||||||
|
//! using the native OS-provided facilities (think `TlsAlloc` or
|
||||||
|
//! `pthread_setspecific`). The interface of this differs from the other types
|
||||||
|
//! of thread-local-storage provided in this crate in that OS-based TLS can only
|
||||||
|
//! get/set pointers,
|
||||||
|
//!
|
||||||
|
//! This module also provides two flavors of TLS. One is intended for static
|
||||||
|
//! initialization, and does not contain a `Drop` implementation to deallocate
|
||||||
|
//! the OS-TLS key. The other is a type which does implement `Drop` and hence
|
||||||
|
//! has a safe interface.
|
||||||
|
//!
|
||||||
|
//! # Usage
|
||||||
|
//!
|
||||||
|
//! This module should likely not be used directly unless other primitives are
|
||||||
|
//! being built on. types such as `thread_local::spawn::Key` are likely much
|
||||||
|
//! more useful in practice than this OS-based version which likely requires
|
||||||
|
//! unsafe code to interoperate with.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! Using a dynamically allocated TLS key. Note that this key can be shared
|
||||||
|
//! among many threads via an `Arc`.
|
||||||
|
//!
|
||||||
|
//! ```rust,ignore
|
||||||
|
//! let key = Key::new(None);
|
||||||
|
//! assert!(key.get().is_null());
|
||||||
|
//! key.set(1 as *mut u8);
|
||||||
|
//! assert!(!key.get().is_null());
|
||||||
|
//!
|
||||||
|
//! drop(key); // deallocate this TLS slot.
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Sometimes a statically allocated key is either required or easier to work
|
||||||
|
//! with, however.
|
||||||
|
//!
|
||||||
|
//! ```rust,ignore
|
||||||
|
//! static KEY: StaticKey = INIT;
|
||||||
|
//!
|
||||||
|
//! unsafe {
|
||||||
|
//! assert!(KEY.get().is_null());
|
||||||
|
//! KEY.set(1 as *mut u8);
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
#![allow(non_camel_case_types)] |
||||||
|
#![unstable(feature = "thread_local_internals", issue = "0")] |
||||||
|
#![allow(dead_code)] // sys isn't exported yet
|
||||||
|
|
||||||
|
use sync::atomic::{self, AtomicUsize, Ordering}; |
||||||
|
|
||||||
|
use sys::thread_local as imp; |
||||||
|
|
||||||
|
/// A type for TLS keys that are statically allocated.
|
||||||
|
///
|
||||||
|
/// This type is entirely `unsafe` to use as it does not protect against
|
||||||
|
/// use-after-deallocation or use-during-deallocation.
|
||||||
|
///
|
||||||
|
/// The actual OS-TLS key is lazily allocated when this is used for the first
|
||||||
|
/// time. The key is also deallocated when the Rust runtime exits or `destroy`
|
||||||
|
/// is called, whichever comes first.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// use tls::os::{StaticKey, INIT};
|
||||||
|
///
|
||||||
|
/// static KEY: StaticKey = INIT;
|
||||||
|
///
|
||||||
|
/// unsafe {
|
||||||
|
/// assert!(KEY.get().is_null());
|
||||||
|
/// KEY.set(1 as *mut u8);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub struct StaticKey { |
||||||
|
/// Inner static TLS key (internals).
|
||||||
|
key: AtomicUsize, |
||||||
|
/// Destructor for the TLS value.
|
||||||
|
///
|
||||||
|
/// See `Key::new` for information about when the destructor runs and how
|
||||||
|
/// it runs.
|
||||||
|
dtor: Option<unsafe extern fn(*mut u8)>, |
||||||
|
} |
||||||
|
|
||||||
|
/// A type for a safely managed OS-based TLS slot.
|
||||||
|
///
|
||||||
|
/// This type allocates an OS TLS key when it is initialized and will deallocate
|
||||||
|
/// the key when it falls out of scope. When compared with `StaticKey`, this
|
||||||
|
/// type is entirely safe to use.
|
||||||
|
///
|
||||||
|
/// Implementations will likely, however, contain unsafe code as this type only
|
||||||
|
/// operates on `*mut u8`, a raw pointer.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// use tls::os::Key;
|
||||||
|
///
|
||||||
|
/// let key = Key::new(None);
|
||||||
|
/// assert!(key.get().is_null());
|
||||||
|
/// key.set(1 as *mut u8);
|
||||||
|
/// assert!(!key.get().is_null());
|
||||||
|
///
|
||||||
|
/// drop(key); // deallocate this TLS slot.
|
||||||
|
/// ```
|
||||||
|
pub struct Key { |
||||||
|
key: imp::Key, |
||||||
|
} |
||||||
|
|
||||||
|
/// Constant initialization value for static TLS keys.
|
||||||
|
///
|
||||||
|
/// This value specifies no destructor by default.
|
||||||
|
pub const INIT: StaticKey = StaticKey::new(None); |
||||||
|
|
||||||
|
impl StaticKey { |
||||||
|
pub const fn new(dtor: Option<unsafe extern fn(*mut u8)>) -> StaticKey { |
||||||
|
StaticKey { |
||||||
|
key: atomic::AtomicUsize::new(0), |
||||||
|
dtor: dtor |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Gets the value associated with this TLS key
|
||||||
|
///
|
||||||
|
/// This will lazily allocate a TLS key from the OS if one has not already
|
||||||
|
/// been allocated.
|
||||||
|
#[inline] |
||||||
|
pub unsafe fn get(&self) -> *mut u8 { imp::get(self.key()) } |
||||||
|
|
||||||
|
/// Sets this TLS key to a new value.
|
||||||
|
///
|
||||||
|
/// This will lazily allocate a TLS key from the OS if one has not already
|
||||||
|
/// been allocated.
|
||||||
|
#[inline] |
||||||
|
pub unsafe fn set(&self, val: *mut u8) { imp::set(self.key(), val) } |
||||||
|
|
||||||
|
/// Deallocates this OS TLS key.
|
||||||
|
///
|
||||||
|
/// This function is unsafe as there is no guarantee that the key is not
|
||||||
|
/// currently in use by other threads or will not ever be used again.
|
||||||
|
///
|
||||||
|
/// Note that this does *not* run the user-provided destructor if one was
|
||||||
|
/// specified at definition time. Doing so must be done manually.
|
||||||
|
pub unsafe fn destroy(&self) { |
||||||
|
match self.key.swap(0, Ordering::SeqCst) { |
||||||
|
0 => {} |
||||||
|
n => { imp::destroy(n as imp::Key) } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
unsafe fn key(&self) -> imp::Key { |
||||||
|
match self.key.load(Ordering::Relaxed) { |
||||||
|
0 => self.lazy_init() as imp::Key, |
||||||
|
n => n as imp::Key |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
unsafe fn lazy_init(&self) -> usize { |
||||||
|
// POSIX allows the key created here to be 0, but the compare_and_swap
|
||||||
|
// below relies on using 0 as a sentinel value to check who won the
|
||||||
|
// race to set the shared TLS key. As far as I know, there is no
|
||||||
|
// guaranteed value that cannot be returned as a posix_key_create key,
|
||||||
|
// so there is no value we can initialize the inner key with to
|
||||||
|
// prove that it has not yet been set. As such, we'll continue using a
|
||||||
|
// value of 0, but with some gyrations to make sure we have a non-0
|
||||||
|
// value returned from the creation routine.
|
||||||
|
// FIXME: this is clearly a hack, and should be cleaned up.
|
||||||
|
let key1 = imp::create(self.dtor); |
||||||
|
let key = if key1 != 0 { |
||||||
|
key1 |
||||||
|
} else { |
||||||
|
let key2 = imp::create(self.dtor); |
||||||
|
imp::destroy(key1); |
||||||
|
key2 |
||||||
|
}; |
||||||
|
assert!(key != 0); |
||||||
|
match self.key.compare_and_swap(0, key as usize, Ordering::SeqCst) { |
||||||
|
// The CAS succeeded, so we've created the actual key
|
||||||
|
0 => key as usize, |
||||||
|
// If someone beat us to the punch, use their key instead
|
||||||
|
n => { imp::destroy(key); n } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Key { |
||||||
|
/// Creates a new managed OS TLS key.
|
||||||
|
///
|
||||||
|
/// This key will be deallocated when the key falls out of scope.
|
||||||
|
///
|
||||||
|
/// The argument provided is an optionally-specified destructor for the
|
||||||
|
/// value of this TLS key. When a thread exits and the value for this key
|
||||||
|
/// is non-null the destructor will be invoked. The TLS value will be reset
|
||||||
|
/// to null before the destructor is invoked.
|
||||||
|
///
|
||||||
|
/// Note that the destructor will not be run when the `Key` goes out of
|
||||||
|
/// scope.
|
||||||
|
#[inline] |
||||||
|
pub fn new(dtor: Option<unsafe extern fn(*mut u8)>) -> Key { |
||||||
|
Key { key: unsafe { imp::create(dtor) } } |
||||||
|
} |
||||||
|
|
||||||
|
/// See StaticKey::get
|
||||||
|
#[inline] |
||||||
|
pub fn get(&self) -> *mut u8 { |
||||||
|
unsafe { imp::get(self.key) } |
||||||
|
} |
||||||
|
|
||||||
|
/// See StaticKey::set
|
||||||
|
#[inline] |
||||||
|
pub fn set(&self, val: *mut u8) { |
||||||
|
unsafe { imp::set(self.key, val) } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Drop for Key { |
||||||
|
fn drop(&mut self) { |
||||||
|
unsafe { imp::destroy(self.key) } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests { |
||||||
|
use super::{Key, StaticKey}; |
||||||
|
|
||||||
|
fn assert_sync<T: Sync>() {} |
||||||
|
fn assert_send<T: Send>() {} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn smoke() { |
||||||
|
assert_sync::<Key>(); |
||||||
|
assert_send::<Key>(); |
||||||
|
|
||||||
|
let k1 = Key::new(None); |
||||||
|
let k2 = Key::new(None); |
||||||
|
assert!(k1.get().is_null()); |
||||||
|
assert!(k2.get().is_null()); |
||||||
|
k1.set(1 as *mut _); |
||||||
|
k2.set(2 as *mut _); |
||||||
|
assert_eq!(k1.get() as usize, 1); |
||||||
|
assert_eq!(k2.get() as usize, 2); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn statik() { |
||||||
|
static K1: StaticKey = StaticKey::new(None); |
||||||
|
static K2: StaticKey = StaticKey::new(None); |
||||||
|
|
||||||
|
unsafe { |
||||||
|
assert!(K1.get().is_null()); |
||||||
|
assert!(K2.get().is_null()); |
||||||
|
K1.set(1 as *mut _); |
||||||
|
K2.set(2 as *mut _); |
||||||
|
assert_eq!(K1.get() as usize, 1); |
||||||
|
assert_eq!(K2.get() as usize, 2); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,596 @@ |
|||||||
|
// 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
|
||||||
|
|
||||||
|
#![unstable(feature = "thread_local_internals", issue = "0")] |
||||||
|
|
||||||
|
use cell::UnsafeCell; |
||||||
|
use fmt; |
||||||
|
use mem; |
||||||
|
|
||||||
|
/// 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 threads 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);
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Platform-specific behavior
|
||||||
|
///
|
||||||
|
/// Note that a "best effort" is made to ensure that destructors for types
|
||||||
|
/// stored in thread local storage are run, but not all platforms can guarantee
|
||||||
|
/// that destructors will be run for all types in thread local storage. For
|
||||||
|
/// example, there are a number of known caveats where destructors are not run:
|
||||||
|
///
|
||||||
|
/// 1. On Unix systems when pthread-based TLS is being used, destructors will
|
||||||
|
/// not be run for TLS values on the main thread when it exits. Note that the
|
||||||
|
/// application will exit immediately after the main thread exits as well.
|
||||||
|
/// 2. On all platforms it's possible for TLS to re-initialize other TLS slots
|
||||||
|
/// during destruction. Some platforms ensure that this cannot happen
|
||||||
|
/// infinitely by preventing re-initialization of any slot that has been
|
||||||
|
/// destroyed, but not all platforms have this guard. Those platforms that do
|
||||||
|
/// not guard typically have a synthetic limit after which point no more
|
||||||
|
/// destructors are run.
|
||||||
|
/// 3. On OSX, initializing TLS during destruction of other TLS slots can
|
||||||
|
/// sometimes cancel *all* destructors for the current thread, whether or not
|
||||||
|
/// the slots have already had their destructors run or not.
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")] |
||||||
|
pub struct LocalKey<T: 'static> { |
||||||
|
// This outer `LocalKey<T>` type is what's going to be stored in statics,
|
||||||
|
// but actual data inside will sometimes be tagged with #[thread_local].
|
||||||
|
// It's not valid for a true static to reference a #[thread_local] static,
|
||||||
|
// so we get around that by exposing an accessor through a layer of function
|
||||||
|
// indirection (this thunk).
|
||||||
|
//
|
||||||
|
// Note that the thunk is itself unsafe because the returned lifetime of the
|
||||||
|
// slot where data lives, `'static`, is not actually valid. The lifetime
|
||||||
|
// here is actually `'thread`!
|
||||||
|
//
|
||||||
|
// Although this is an extra layer of indirection, it should in theory be
|
||||||
|
// trivially devirtualizable by LLVM because the value of `inner` never
|
||||||
|
// changes and the constant should be readonly within a crate. This mainly
|
||||||
|
// only runs into problems when TLS statics are exported across crates.
|
||||||
|
inner: fn() -> Option<&'static UnsafeCell<Option<T>>>, |
||||||
|
|
||||||
|
// initialization routine to invoke to create a value
|
||||||
|
init: fn() -> T, |
||||||
|
} |
||||||
|
|
||||||
|
#[stable(feature = "std_debug", since = "1.16.0")] |
||||||
|
impl<T: 'static> fmt::Debug for LocalKey<T> { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||||
|
f.pad("LocalKey { .. }") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Declare a new thread local storage key of type `std::thread::LocalKey`.
|
||||||
|
///
|
||||||
|
/// # Syntax
|
||||||
|
///
|
||||||
|
/// The macro wraps any number of static declarations and makes them thread local.
|
||||||
|
/// Each static may be public or private, and attributes are allowed. Example:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::cell::RefCell;
|
||||||
|
/// thread_local! {
|
||||||
|
/// pub static FOO: RefCell<u32> = RefCell::new(1);
|
||||||
|
///
|
||||||
|
/// #[allow(unused)]
|
||||||
|
/// static BAR: RefCell<f32> = RefCell::new(1.0);
|
||||||
|
/// }
|
||||||
|
/// # fn main() {}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// See [LocalKey documentation](thread/struct.LocalKey.html) for more
|
||||||
|
/// information.
|
||||||
|
#[macro_export] |
||||||
|
#[stable(feature = "rust1", since = "1.0.0")] |
||||||
|
#[allow_internal_unstable] |
||||||
|
macro_rules! thread_local { |
||||||
|
// rule 0: empty (base case for the recursion)
|
||||||
|
() => {}; |
||||||
|
|
||||||
|
// rule 1: process multiple declarations where the first one is private
|
||||||
|
($(#[$attr:meta])* static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( |
||||||
|
thread_local!($(#[$attr])* static $name: $t = $init); // go to rule 2
|
||||||
|
thread_local!($($rest)*); |
||||||
|
); |
||||||
|
|
||||||
|
// rule 2: handle a single private declaration
|
||||||
|
($(#[$attr:meta])* static $name:ident: $t:ty = $init:expr) => ( |
||||||
|
$(#[$attr])* static $name: $crate::thread::LocalKey<$t> = |
||||||
|
__thread_local_inner!($t, $init); |
||||||
|
); |
||||||
|
|
||||||
|
// rule 3: handle multiple declarations where the first one is public
|
||||||
|
($(#[$attr:meta])* pub static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( |
||||||
|
thread_local!($(#[$attr])* pub static $name: $t = $init); // go to rule 4
|
||||||
|
thread_local!($($rest)*); |
||||||
|
); |
||||||
|
|
||||||
|
// rule 4: handle a single public declaration
|
||||||
|
($(#[$attr:meta])* pub static $name:ident: $t:ty = $init:expr) => ( |
||||||
|
$(#[$attr])* pub static $name: $crate::thread::LocalKey<$t> = |
||||||
|
__thread_local_inner!($t, $init); |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[doc(hidden)] |
||||||
|
#[unstable(feature = "thread_local_internals",
|
||||||
|
reason = "should not be necessary", |
||||||
|
issue = "0")] |
||||||
|
#[macro_export] |
||||||
|
#[allow_internal_unstable] |
||||||
|
macro_rules! __thread_local_inner { |
||||||
|
($t:ty, $init:expr) => {{ |
||||||
|
fn __init() -> $t { $init } |
||||||
|
|
||||||
|
fn __getit() -> $crate::option::Option< |
||||||
|
&'static $crate::cell::UnsafeCell< |
||||||
|
$crate::option::Option<$t>>> |
||||||
|
{ |
||||||
|
#[thread_local] |
||||||
|
#[cfg(target_thread_local)] |
||||||
|
static __KEY: $crate::thread::__FastLocalKeyInner<$t> = |
||||||
|
$crate::thread::__FastLocalKeyInner::new(); |
||||||
|
|
||||||
|
#[cfg(not(target_thread_local))] |
||||||
|
static __KEY: $crate::thread::__OsLocalKeyInner<$t> = |
||||||
|
$crate::thread::__OsLocalKeyInner::new(); |
||||||
|
|
||||||
|
__KEY.get() |
||||||
|
} |
||||||
|
|
||||||
|
$crate::thread::LocalKey::new(__getit, __init) |
||||||
|
}} |
||||||
|
} |
||||||
|
|
||||||
|
/// Indicator of the state of a thread local storage key.
|
||||||
|
#[unstable(feature = "thread_local_state",
|
||||||
|
reason = "state querying was recently added", |
||||||
|
issue = "27716")] |
||||||
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)] |
||||||
|
pub enum LocalKeyState { |
||||||
|
/// 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> LocalKey<T> { |
||||||
|
#[doc(hidden)] |
||||||
|
#[unstable(feature = "thread_local_internals",
|
||||||
|
reason = "recently added to create a key", |
||||||
|
issue = "0")] |
||||||
|
pub const fn new(inner: fn() -> Option<&'static UnsafeCell<Option<T>>>, |
||||||
|
init: fn() -> T) -> LocalKey<T> { |
||||||
|
LocalKey { |
||||||
|
inner: inner, |
||||||
|
init: init, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// # 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 { |
||||||
|
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), |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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(); |
||||||
|
|
||||||
|
// note that this can in theory just be `*ptr = Some(value)`, but due to
|
||||||
|
// the compiler will currently codegen that pattern with something like:
|
||||||
|
//
|
||||||
|
// ptr::drop_in_place(ptr)
|
||||||
|
// ptr::write(ptr, Some(value))
|
||||||
|
//
|
||||||
|
// Due to this pattern it's possible for the destructor of the value in
|
||||||
|
// `ptr` (e.g. if this is being recursively initialized) to re-access
|
||||||
|
// TLS, in which case there will be a `&` and `&mut` pointer to the same
|
||||||
|
// value (an aliasing violation). To avoid setting the "I'm running a
|
||||||
|
// destructor" flag we just use `mem::replace` which should sequence the
|
||||||
|
// operations a little differently and make this safe to call.
|
||||||
|
mem::replace(&mut *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` state 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 = "thread_local_state",
|
||||||
|
reason = "state querying was recently added", |
||||||
|
issue = "27716")] |
||||||
|
pub fn state(&'static self) -> LocalKeyState { |
||||||
|
unsafe { |
||||||
|
match (self.inner)() { |
||||||
|
Some(cell) => { |
||||||
|
match *cell.get() { |
||||||
|
Some(..) => LocalKeyState::Valid, |
||||||
|
None => LocalKeyState::Uninitialized, |
||||||
|
} |
||||||
|
} |
||||||
|
None => LocalKeyState::Destroyed, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[doc(hidden)] |
||||||
|
pub mod os { |
||||||
|
use cell::{Cell, UnsafeCell}; |
||||||
|
use fmt; |
||||||
|
use marker; |
||||||
|
use ptr; |
||||||
|
use sys_common::thread_local::StaticKey as OsStaticKey; |
||||||
|
|
||||||
|
pub struct Key<T> { |
||||||
|
// OS-TLS key that we'll use to key off.
|
||||||
|
os: OsStaticKey, |
||||||
|
marker: marker::PhantomData<Cell<T>>, |
||||||
|
} |
||||||
|
|
||||||
|
impl<T> fmt::Debug for Key<T> { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||||
|
f.pad("Key { .. }") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
unsafe impl<T> ::marker::Sync for Key<T> { } |
||||||
|
|
||||||
|
struct Value<T: 'static> { |
||||||
|
key: &'static Key<T>, |
||||||
|
value: UnsafeCell<Option<T>>, |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: 'static> Key<T> { |
||||||
|
pub const fn new() -> Key<T> { |
||||||
|
Key { |
||||||
|
os: OsStaticKey::new(Some(destroy_value::<T>)), |
||||||
|
marker: marker::PhantomData |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> { |
||||||
|
unsafe { |
||||||
|
let ptr = self.os.get() as *mut Value<T>; |
||||||
|
if !ptr.is_null() { |
||||||
|
if ptr as usize == 1 { |
||||||
|
return None |
||||||
|
} |
||||||
|
return Some(&(*ptr).value); |
||||||
|
} |
||||||
|
|
||||||
|
// If the lookup returned null, we haven't initialized our own local
|
||||||
|
// copy, so do that now.
|
||||||
|
let ptr: Box<Value<T>> = box Value { |
||||||
|
key: self, |
||||||
|
value: UnsafeCell::new(None), |
||||||
|
}; |
||||||
|
let ptr = Box::into_raw(ptr); |
||||||
|
self.os.set(ptr as *mut u8); |
||||||
|
Some(&(*ptr).value) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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::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(all(test, not(target_os = "emscripten")))] |
||||||
|
mod tests { |
||||||
|
use sync::mpsc::{channel, Sender}; |
||||||
|
use cell::{Cell, UnsafeCell}; |
||||||
|
use super::LocalKeyState; |
||||||
|
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: Cell<i32> = Cell::new(1)); |
||||||
|
|
||||||
|
FOO.with(|f| { |
||||||
|
assert_eq!(f.get(), 1); |
||||||
|
f.set(2); |
||||||
|
}); |
||||||
|
let (tx, rx) = channel(); |
||||||
|
let _t = thread::spawn(move|| { |
||||||
|
FOO.with(|f| { |
||||||
|
assert_eq!(f.get(), 1); |
||||||
|
}); |
||||||
|
tx.send(()).unwrap(); |
||||||
|
}); |
||||||
|
rx.recv().unwrap(); |
||||||
|
|
||||||
|
FOO.with(|f| { |
||||||
|
assert_eq!(f.get(), 2); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn states() { |
||||||
|
struct Foo; |
||||||
|
impl Drop for Foo { |
||||||
|
fn drop(&mut self) { |
||||||
|
assert!(FOO.state() == LocalKeyState::Destroyed); |
||||||
|
} |
||||||
|
} |
||||||
|
fn foo() -> Foo { |
||||||
|
assert!(FOO.state() == LocalKeyState::Uninitialized); |
||||||
|
Foo |
||||||
|
} |
||||||
|
thread_local!(static FOO: Foo = foo()); |
||||||
|
|
||||||
|
thread::spawn(|| { |
||||||
|
assert!(FOO.state() == LocalKeyState::Uninitialized); |
||||||
|
FOO.with(|_| { |
||||||
|
assert!(FOO.state() == LocalKeyState::Valid); |
||||||
|
}); |
||||||
|
assert!(FOO.state() == LocalKeyState::Valid); |
||||||
|
}).join().ok().unwrap(); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn smoke_dtor() { |
||||||
|
thread_local!(static FOO: UnsafeCell<Option<Foo>> = UnsafeCell::new(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::new(None)); |
||||||
|
thread_local!(static K2: UnsafeCell<Option<S2>> = UnsafeCell::new(None)); |
||||||
|
static mut HITS: u32 = 0; |
||||||
|
|
||||||
|
impl Drop for S1 { |
||||||
|
fn drop(&mut self) { |
||||||
|
unsafe { |
||||||
|
HITS += 1; |
||||||
|
if K2.state() == LocalKeyState::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() != LocalKeyState::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::new(None)); |
||||||
|
|
||||||
|
impl Drop for S1 { |
||||||
|
fn drop(&mut self) { |
||||||
|
assert!(K1.state() == LocalKeyState::Destroyed); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
thread::spawn(move|| unsafe { |
||||||
|
K1.with(|s| *s.get() = Some(S1)); |
||||||
|
}).join().ok().unwrap(); |
||||||
|
} |
||||||
|
|
||||||
|
// Note that this test will deadlock if TLS destructors aren't run (this
|
||||||
|
// requires the destructor to be run to pass the test). OSX has a known bug
|
||||||
|
// where dtors-in-dtors may cancel other destructors, so we just ignore this
|
||||||
|
// test on OSX.
|
||||||
|
#[test] |
||||||
|
#[cfg_attr(target_os = "macos", ignore)] |
||||||
|
fn dtors_in_dtors_in_dtors() { |
||||||
|
struct S1(Sender<()>); |
||||||
|
thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None)); |
||||||
|
thread_local!(static K2: UnsafeCell<Option<Foo>> = UnsafeCell::new(None)); |
||||||
|
|
||||||
|
impl Drop for S1 { |
||||||
|
fn drop(&mut self) { |
||||||
|
let S1(ref tx) = *self; |
||||||
|
unsafe { |
||||||
|
if K2.state() != LocalKeyState::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 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); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,195 @@ |
|||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
//! Native threads.
|
||||||
|
//!
|
||||||
|
//! ## The threading model
|
||||||
|
//!
|
||||||
|
//! An executing Rust program consists of a collection of native OS threads,
|
||||||
|
//! each with their own stack and local state. Threads can be named, and
|
||||||
|
//! provide some built-in support for low-level synchronization.
|
||||||
|
//!
|
||||||
|
//! Communication between threads can be done through
|
||||||
|
//! [channels], Rust's message-passing types, along with [other forms of thread
|
||||||
|
//! synchronization](../../std/sync/index.html) and shared-memory data
|
||||||
|
//! structures. In particular, types that are guaranteed to be
|
||||||
|
//! threadsafe are easily shared between threads using the
|
||||||
|
//! atomically-reference-counted container, [`Arc`].
|
||||||
|
//!
|
||||||
|
//! Fatal logic errors in Rust cause *thread panic*, during which
|
||||||
|
//! a thread will unwind the stack, running destructors and freeing
|
||||||
|
//! owned resources. Thread panic is unrecoverable from within
|
||||||
|
//! the panicking thread (i.e. there is no 'try/catch' in Rust), but
|
||||||
|
//! the panic may optionally be detected from a different thread. If
|
||||||
|
//! the main thread panics, the application will exit with a non-zero
|
||||||
|
//! exit code.
|
||||||
|
//!
|
||||||
|
//! When the main thread of a Rust program terminates, the entire program shuts
|
||||||
|
//! down, even if other threads are still running. However, this module provides
|
||||||
|
//! convenient facilities for automatically waiting for the termination of a
|
||||||
|
//! child thread (i.e., join).
|
||||||
|
//!
|
||||||
|
//! ## Spawning a thread
|
||||||
|
//!
|
||||||
|
//! A new thread can be spawned using the [`thread::spawn`][`spawn`] function:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use std::thread;
|
||||||
|
//!
|
||||||
|
//! thread::spawn(move || {
|
||||||
|
//! // some work here
|
||||||
|
//! });
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! In this example, the spawned thread is "detached" from the current
|
||||||
|
//! thread. This means that it can outlive its parent (the thread that spawned
|
||||||
|
//! it), unless this parent is the main thread.
|
||||||
|
//!
|
||||||
|
//! The parent thread can also wait on the completion of the child
|
||||||
|
//! thread; a call to [`spawn`] produces a [`JoinHandle`], which provides
|
||||||
|
//! a `join` method for waiting:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use std::thread;
|
||||||
|
//!
|
||||||
|
//! let child = thread::spawn(move || {
|
||||||
|
//! // some work here
|
||||||
|
//! });
|
||||||
|
//! // some work here
|
||||||
|
//! let res = child.join();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! The [`join`] method returns a [`Result`] containing [`Ok`] of the final
|
||||||
|
//! value produced by the child thread, or [`Err`] of the value given to
|
||||||
|
//! a call to [`panic!`] if the child panicked.
|
||||||
|
//!
|
||||||
|
//! ## Configuring threads
|
||||||
|
//!
|
||||||
|
//! A new thread can be configured before it is spawned via the [`Builder`] type,
|
||||||
|
//! which currently allows you to set the name and stack size for the child thread:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! # #![allow(unused_must_use)]
|
||||||
|
//! use std::thread;
|
||||||
|
//!
|
||||||
|
//! thread::Builder::new().name("child1".to_string()).spawn(move || {
|
||||||
|
//! println!("Hello, world!");
|
||||||
|
//! });
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## The `Thread` type
|
||||||
|
//!
|
||||||
|
//! Threads are represented via the [`Thread`] type, which you can get in one of
|
||||||
|
//! two ways:
|
||||||
|
//!
|
||||||
|
//! * By spawning a new thread, e.g. using the [`thread::spawn`][`spawn`]
|
||||||
|
//! function, and calling [`thread()`] on the [`JoinHandle`].
|
||||||
|
//! * By requesting the current thread, using the [`thread::current()`] function.
|
||||||
|
//!
|
||||||
|
//! The [`thread::current()`] function is available even for threads not spawned
|
||||||
|
//! by the APIs of this module.
|
||||||
|
//!
|
||||||
|
//! ## Blocking support: park and unpark
|
||||||
|
//!
|
||||||
|
//! Every thread is equipped with some basic low-level blocking support, via the
|
||||||
|
//! [`thread::park()`][`park()`] function and [`thread::Thread::unpark()`][`unpark()`]
|
||||||
|
//! method. [`park()`] blocks the current thread, which can then be resumed from
|
||||||
|
//! another thread by calling the [`unpark()`] method on the blocked thread's handle.
|
||||||
|
//!
|
||||||
|
//! Conceptually, each [`Thread`] handle has an associated token, which is
|
||||||
|
//! initially not present:
|
||||||
|
//!
|
||||||
|
//! * The [`thread::park()`][`park()`] function blocks the current thread unless or until
|
||||||
|
//! the token is available for its thread handle, at which point it atomically
|
||||||
|
//! consumes the token. It may also return *spuriously*, without consuming the
|
||||||
|
//! token. [`thread::park_timeout()`] does the same, but allows specifying a
|
||||||
|
//! maximum time to block the thread for.
|
||||||
|
//!
|
||||||
|
//! * The [`unpark()`] method on a [`Thread`] atomically makes the token available
|
||||||
|
//! if it wasn't already.
|
||||||
|
//!
|
||||||
|
//! In other words, each [`Thread`] acts a bit like a semaphore with initial count
|
||||||
|
//! 0, except that the semaphore is *saturating* (the count cannot go above 1),
|
||||||
|
//! and can return spuriously.
|
||||||
|
//!
|
||||||
|
//! The API is typically used by acquiring a handle to the current thread,
|
||||||
|
//! placing that handle in a shared data structure so that other threads can
|
||||||
|
//! find it, and then `park`ing. When some desired condition is met, another
|
||||||
|
//! thread calls [`unpark()`] on the handle.
|
||||||
|
//!
|
||||||
|
//! The motivation for this design is twofold:
|
||||||
|
//!
|
||||||
|
//! * It avoids the need to allocate mutexes and condvars when building new
|
||||||
|
//! synchronization primitives; the threads already provide basic blocking/signaling.
|
||||||
|
//!
|
||||||
|
//! * It can be implemented very efficiently on many platforms.
|
||||||
|
//!
|
||||||
|
//! ## Thread-local storage
|
||||||
|
//!
|
||||||
|
//! This module also provides an implementation of thread-local storage for Rust
|
||||||
|
//! programs. Thread-local storage is a method of storing data into a global
|
||||||
|
//! variable that 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.
|
||||||
|
//!
|
||||||
|
//! A thread-local key owns the value it contains and will destroy the value when the
|
||||||
|
//! thread exits. It is created with the [`thread_local!`] macro and can contain any
|
||||||
|
//! value that is `'static` (no borrowed pointers). It provides an accessor function,
|
||||||
|
//! [`with`], that yields a shared reference to the value to the specified
|
||||||
|
//! closure. Thread-local keys allow only shared access to values, as there would be no
|
||||||
|
//! way to guarantee uniqueness if mutable borrows were allowed. Most values
|
||||||
|
//! will want to make use of some form of **interior mutability** through the
|
||||||
|
//! [`Cell`] or [`RefCell`] types.
|
||||||
|
//!
|
||||||
|
//! [channels]: ../../std/sync/mpsc/index.html
|
||||||
|
//! [`Arc`]: ../../std/sync/struct.Arc.html
|
||||||
|
//! [`spawn`]: ../../std/thread/fn.spawn.html
|
||||||
|
//! [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html
|
||||||
|
//! [`thread()`]: ../../std/thread/struct.JoinHandle.html#method.thread
|
||||||
|
//! [`join`]: ../../std/thread/struct.JoinHandle.html#method.join
|
||||||
|
//! [`Result`]: ../../std/result/enum.Result.html
|
||||||
|
//! [`Ok`]: ../../std/result/enum.Result.html#variant.Ok
|
||||||
|
//! [`Err`]: ../../std/result/enum.Result.html#variant.Err
|
||||||
|
//! [`panic!`]: ../../std/macro.panic.html
|
||||||
|
//! [`Builder`]: ../../std/thread/struct.Builder.html
|
||||||
|
//! [`thread::current()`]: ../../std/thread/fn.spawn.html
|
||||||
|
//! [`Thread`]: ../../std/thread/struct.Thread.html
|
||||||
|
//! [`park()`]: ../../std/thread/fn.park.html
|
||||||
|
//! [`unpark()`]: ../../std/thread/struct.Thread.html#method.unpark
|
||||||
|
//! [`thread::park_timeout()`]: ../../std/thread/fn.park_timeout.html
|
||||||
|
//! [`Cell`]: ../cell/struct.Cell.html
|
||||||
|
//! [`RefCell`]: ../cell/struct.RefCell.html
|
||||||
|
//! [`thread_local!`]: ../macro.thread_local.html
|
||||||
|
//! [`with`]: struct.LocalKey.html#method.with
|
||||||
|
|
||||||
|
#![stable(feature = "rust1", since = "1.0.0")] |
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Thread-local storage
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#[macro_use] mod local; |
||||||
|
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")] |
||||||
|
pub use self::local::{LocalKey, LocalKeyState}; |
||||||
|
|
||||||
|
// 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
|
||||||
|
// type is accessed via platform-specific API calls and is slow, while the fast
|
||||||
|
// key type is accessed via code generated via LLVM, where TLS keys are set up
|
||||||
|
// by the elf linker. Note that the OS TLS type is always available: on macOS
|
||||||
|
// the standard library is compiled with support for older platform versions
|
||||||
|
// where fast TLS was not available; end-user code is compiled with fast TLS
|
||||||
|
// where available, but both are needed.
|
||||||
|
|
||||||
|
#[unstable(feature = "libstd_thread_internals", issue = "0")] |
||||||
|
#[cfg(target_thread_local)] |
||||||
|
#[doc(hidden)] pub use sys::fast_thread_local::Key as __FastLocalKeyInner; |
||||||
|
#[unstable(feature = "libstd_thread_internals", issue = "0")] |
||||||
|
#[doc(hidden)] pub use self::local::os::Key as __OsLocalKeyInner; |
Loading…
Reference in new issue