Fenrir
8 years ago
8 changed files with 1249 additions and 0 deletions
@ -0,0 +1,116 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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