aboutsummaryrefslogtreecommitdiff
path: root/ctr-std/src/thread
diff options
context:
space:
mode:
authorFenrir <[email protected]>2018-01-21 14:06:28 -0700
committerFenrirWolf <[email protected]>2018-01-21 19:16:33 -0700
commit23be3f4885688e5e0011005e2295c75168854c0a (patch)
treedd0850f9c73c489e114a761d5c0757f3dbec3a65 /ctr-std/src/thread
parentUpdate CI for Rust nightly-2017-12-01 + other fixes (diff)
downloadctru-rs-23be3f4885688e5e0011005e2295c75168854c0a.tar.xz
ctru-rs-23be3f4885688e5e0011005e2295c75168854c0a.zip
Recreate ctr-std from latest nightly
Diffstat (limited to 'ctr-std/src/thread')
-rw-r--r--ctr-std/src/thread/local.rs87
-rw-r--r--ctr-std/src/thread/mod.rs201
2 files changed, 207 insertions, 81 deletions
diff --git a/ctr-std/src/thread/local.rs b/ctr-std/src/thread/local.rs
index 0172f89..fcbca38 100644
--- a/ctr-std/src/thread/local.rs
+++ b/ctr-std/src/thread/local.rs
@@ -31,6 +31,10 @@ use mem;
/// within a thread, and values that implement [`Drop`] get destructed when a
/// thread exits. Some caveats apply, which are explained below.
///
+/// A `LocalKey`'s initializer cannot recursively depend on itself, and using
+/// a `LocalKey` in this way will cause the initializer to infinitely recurse
+/// on the first call to `with`.
+///
/// # Examples
///
/// ```
@@ -91,13 +95,13 @@ pub struct LocalKey<T: 'static> {
//
// 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`!
+ // here is actually slightly shorter than the currently running 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>>>,
+ inner: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>,
// initialization routine to invoke to create a value
init: fn() -> T,
@@ -157,12 +161,14 @@ macro_rules! thread_local {
issue = "0")]
#[macro_export]
#[allow_internal_unstable]
+#[allow_internal_unsafe]
macro_rules! __thread_local_inner {
- ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => {
- $(#[$attr])* $vis static $name: $crate::thread::LocalKey<$t> = {
+ (@key $(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => {
+ {
+ #[inline]
fn __init() -> $t { $init }
- fn __getit() -> $crate::option::Option<
+ unsafe fn __getit() -> $crate::option::Option<
&'static $crate::cell::UnsafeCell<
$crate::option::Option<$t>>>
{
@@ -178,8 +184,14 @@ macro_rules! __thread_local_inner {
__KEY.get()
}
- $crate::thread::LocalKey::new(__getit, __init)
- };
+ unsafe {
+ $crate::thread::LocalKey::new(__getit, __init)
+ }
+ }
+ };
+ ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => {
+ $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
+ __thread_local_inner!(@key $(#[$attr])* $vis $name, $t, $init);
}
}
@@ -252,11 +264,11 @@ impl<T: 'static> LocalKey<T> {
#[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> {
+ pub const unsafe fn new(inner: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>,
+ init: fn() -> T) -> LocalKey<T> {
LocalKey {
- inner: inner,
- init: init,
+ inner,
+ init,
}
}
@@ -308,7 +320,10 @@ impl<T: 'static> LocalKey<T> {
///
/// 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.
+ /// succeed within the thread. Some keys might skip the `Uninitialized`
+ /// state altogether and start in the `Valid` state as an optimization
+ /// (e.g. keys initialized with a constant expression), but no guarantees
+ /// are made.
///
/// 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
@@ -391,8 +406,6 @@ pub mod fast {
}
}
- unsafe impl<T> ::marker::Sync for Key<T> { }
-
impl<T> Key<T> {
pub const fn new() -> Key<T> {
Key {
@@ -402,14 +415,12 @@ pub mod fast {
}
}
- pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> {
- unsafe {
- if mem::needs_drop::<T>() && self.dtor_running.get() {
- return None
- }
- self.register_dtor();
+ pub unsafe fn get(&self) -> Option<&'static UnsafeCell<Option<T>>> {
+ if mem::needs_drop::<T>() && self.dtor_running.get() {
+ return None
}
- Some(&self.inner)
+ self.register_dtor();
+ Some(&*(&self.inner as *const _))
}
unsafe fn register_dtor(&self) {
@@ -478,26 +489,24 @@ pub mod os {
}
}
- 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);
+ pub unsafe fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> {
+ let ptr = self.os.get() as *mut Value<T>;
+ if !ptr.is_null() {
+ if ptr as usize == 1 {
+ return None
}
-
- // 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)
+ 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)
}
}
diff --git a/ctr-std/src/thread/mod.rs b/ctr-std/src/thread/mod.rs
index c35676f..ee49bf7 100644
--- a/ctr-std/src/thread/mod.rs
+++ b/ctr-std/src/thread/mod.rs
@@ -25,11 +25,15 @@
//!
//! 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.
+//! owned resources. While not meant as a 'try/catch' mechanism, panics
+//! in Rust can nonetheless be caught (unless compiling with `panic=abort`) with
+//! [`catch_unwind`](../../std/panic/fn.catch_unwind.html) and recovered
+//! from, or alternatively be resumed with
+//! [`resume_unwind`](../../std/panic/fn.resume_unwind.html). If the panic
+//! is not caught the thread will exit, but the panic may optionally be
+//! detected from a different thread with [`join`]. If the main thread panics
+//! without the panic being caught, 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
@@ -112,6 +116,29 @@
//! will want to make use of some form of **interior mutability** through the
//! [`Cell`] or [`RefCell`] types.
//!
+//! ## Naming threads
+//!
+//! Threads are able to have associated names for identification purposes. By default, spawned
+//! threads are unnamed. To specify a name for a thread, build the thread with [`Builder`] and pass
+//! the desired thread name to [`Builder::name`]. To retrieve the thread name from within the
+//! thread, use [`Thread::name`]. A couple examples of where the name of a thread gets used:
+//!
+//! * If a panic occurs in a named thread, the thread name will be printed in the panic message.
+//! * The thread name is provided to the OS where applicable (e.g. `pthread_setname_np` in
+//! unix-like platforms).
+//!
+//! ## Stack size
+//!
+//! The default stack size for spawned threads is 2 MiB, though this particular stack size is
+//! subject to change in the future. There are two ways to manually specify the stack size for
+//! spawned threads:
+//!
+//! * Build the thread with [`Builder`] and pass the desired stack size to [`Builder::stack_size`].
+//! * Set the `RUST_MIN_STACK` environment variable to an integer representing the desired stack
+//! size (in bytes). Note that setting [`Builder::stack_size`] will override this.
+//!
+//! Note that the stack size of the main thread is *not* determined by Rust.
+//!
//! [channels]: ../../std/sync/mpsc/index.html
//! [`Arc`]: ../../std/sync/struct.Arc.html
//! [`spawn`]: ../../std/thread/fn.spawn.html
@@ -123,11 +150,14 @@
//! [`Err`]: ../../std/result/enum.Result.html#variant.Err
//! [`panic!`]: ../../std/macro.panic.html
//! [`Builder`]: ../../std/thread/struct.Builder.html
+//! [`Builder::stack_size`]: ../../std/thread/struct.Builder.html#method.stack_size
+//! [`Builder::name`]: ../../std/thread/struct.Builder.html#method.name
//! [`thread::current`]: ../../std/thread/fn.current.html
//! [`thread::Result`]: ../../std/thread/type.Result.html
//! [`Thread`]: ../../std/thread/struct.Thread.html
//! [`park`]: ../../std/thread/fn.park.html
//! [`unpark`]: ../../std/thread/struct.Thread.html#method.unpark
+//! [`Thread::name`]: ../../std/thread/struct.Thread.html#method.name
//! [`thread::park_timeout`]: ../../std/thread/fn.park_timeout.html
//! [`Cell`]: ../cell/struct.Cell.html
//! [`RefCell`]: ../cell/struct.RefCell.html
@@ -145,10 +175,12 @@ use panic;
use panicking;
use str;
use sync::{Mutex, Condvar, Arc};
+use sync::atomic::AtomicUsize;
+use sync::atomic::Ordering::SeqCst;
use sys::thread as imp;
use sys_common::mutex;
use sys_common::thread_info;
-use sys_common::util;
+use sys_common::thread;
use sys_common::{AsInner, IntoInner};
use time::Duration;
@@ -187,16 +219,8 @@ pub use self::local::{LocalKey, LocalKeyState, AccessError};
///
/// The two configurations available are:
///
-/// - [`name`]: allows to give a name to the thread which is currently
-/// only used in `panic` messages.
-/// - [`stack_size`]: specifies the desired stack size. Note that this can
-/// be overridden by the OS.
-///
-/// If the [`stack_size`] field is not specified, the stack size
-/// will be the `RUST_MIN_STACK` environment variable. If it is
-/// not specified either, a sensible default will be set.
-///
-/// If the [`name`] field is not specified, the thread will not be named.
+/// - [`name`]: specifies an [associated name for the thread][naming-threads]
+/// - [`stack_size`]: specifies the [desired stack size for the thread][stack-size]
///
/// The [`spawn`] method will take ownership of the builder and create an
/// [`io::Result`] to the thread handle with the given configuration.
@@ -228,6 +252,8 @@ pub use self::local::{LocalKey, LocalKeyState, AccessError};
/// [`spawn`]: ../../std/thread/struct.Builder.html#method.spawn
/// [`io::Result`]: ../../std/io/type.Result.html
/// [`unwrap`]: ../../std/result/enum.Result.html#method.unwrap
+/// [naming-threads]: ./index.html#naming-threads
+/// [stack-size]: ./index.html#stack-size
#[stable(feature = "rust1", since = "1.0.0")]
#[derive(Debug)]
pub struct Builder {
@@ -267,6 +293,11 @@ impl Builder {
/// Names the thread-to-be. Currently the name is used for identification
/// only in panic messages.
///
+ /// The name must not contain null bytes (`\0`).
+ ///
+ /// For more information about named threads, see
+ /// [this module-level documentation][naming-threads].
+ ///
/// # Examples
///
/// ```
@@ -281,6 +312,8 @@ impl Builder {
///
/// handler.join().unwrap();
/// ```
+ ///
+ /// [naming-threads]: ./index.html#naming-threads
#[stable(feature = "rust1", since = "1.0.0")]
pub fn name(mut self, name: String) -> Builder {
self.name = Some(name);
@@ -292,6 +325,9 @@ impl Builder {
/// The actual stack size may be greater than this value if
/// the platform specifies minimal stack size.
///
+ /// For more information about the stack size for threads, see
+ /// [this module-level documentation][stack-size].
+ ///
/// # Examples
///
/// ```
@@ -299,6 +335,8 @@ impl Builder {
///
/// let builder = thread::Builder::new().stack_size(32 * 1024);
/// ```
+ ///
+ /// [stack-size]: ./index.html#stack-size
#[stable(feature = "rust1", since = "1.0.0")]
pub fn stack_size(mut self, size: usize) -> Builder {
self.stack_size = Some(size);
@@ -325,6 +363,10 @@ impl Builder {
/// [`io::Result`]: ../../std/io/type.Result.html
/// [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html
///
+ /// # Panics
+ ///
+ /// Panics if a thread name was set and it contained null bytes.
+ ///
/// # Examples
///
/// ```
@@ -344,7 +386,7 @@ impl Builder {
{
let Builder { name, stack_size } = self;
- let stack_size = stack_size.unwrap_or(util::min_stack());
+ let stack_size = stack_size.unwrap_or_else(thread::min_stack);
let my_thread = Thread::new(name);
let their_thread = my_thread.clone();
@@ -413,7 +455,7 @@ impl Builder {
/// *by value* from the thread where it is spawned to the new thread. Its
/// return value will need to be passed from the new thread to the thread
/// where it is `join`ed.
-/// As a reminder, the [`Send`] marker trait, expresses that it is safe to be
+/// As a reminder, the [`Send`] marker trait expresses that it is safe to be
/// passed from thread to thread. [`Sync`] expresses that it is safe to have a
/// reference be passed from thread to thread.
///
@@ -449,15 +491,17 @@ impl Builder {
/// let (tx, rx) = channel();
///
/// let sender = thread::spawn(move || {
-/// let _ = tx.send("Hello, thread".to_owned());
+/// tx.send("Hello, thread".to_owned())
+/// .expect("Unable to send on channel");
/// });
///
/// let receiver = thread::spawn(move || {
-/// println!("{}", rx.recv().unwrap());
+/// let value = rx.recv().expect("Unable to receive from channel");
+/// println!("{}", value);
/// });
///
-/// let _ = sender.join();
-/// let _ = receiver.join();
+/// sender.join().expect("The sender thread has panicked");
+/// receiver.join().expect("The receiver thread has panicked");
/// ```
///
/// A thread can also return a value through its [`JoinHandle`], you can use
@@ -530,8 +574,8 @@ pub fn current() -> Thread {
/// implementing low-level shared resources or synchronization primitives.
///
/// However programmers will usually prefer to use, [`channel`]s, [`Condvar`]s,
-/// [`Mutex`]es or [`join`] for their synchronisation routines, as they avoid
-/// thinking about thread schedulling.
+/// [`Mutex`]es or [`join`] for their synchronization routines, as they avoid
+/// thinking about thread scheduling.
///
/// Note that [`channel`]s for example are implemented using this primitive.
/// Indeed when you call `send` or `recv`, which are blocking, they will yield
@@ -656,6 +700,11 @@ pub fn sleep(dur: Duration) {
imp::Thread::sleep(dur)
}
+// constants for park/unpark
+const EMPTY: usize = 0;
+const PARKED: usize = 1;
+const NOTIFIED: usize = 2;
+
/// Blocks unless or until the current thread's token is made available.
///
/// A call to `park` does not guarantee that the thread will remain parked
@@ -733,11 +782,27 @@ pub fn sleep(dur: Duration) {
#[stable(feature = "rust1", since = "1.0.0")]
pub fn park() {
let thread = current();
- let mut guard = thread.inner.lock.lock().unwrap();
- while !*guard {
- guard = thread.inner.cvar.wait(guard).unwrap();
+
+ // If we were previously notified then we consume this notification and
+ // return quickly.
+ if thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
+ return
+ }
+
+ // Otherwise we need to coordinate going to sleep
+ let mut m = thread.inner.lock.lock().unwrap();
+ match thread.inner.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
+ Ok(_) => {}
+ Err(NOTIFIED) => return, // notified after we locked
+ Err(_) => panic!("inconsistent park state"),
+ }
+ loop {
+ m = thread.inner.cvar.wait(m).unwrap();
+ match thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) {
+ Ok(_) => return, // got a notification
+ Err(_) => {} // spurious wakeup, go back to sleep
+ }
}
- *guard = false;
}
/// Use [`park_timeout`].
@@ -777,7 +842,7 @@ pub fn park_timeout_ms(ms: u32) {
/// Platforms which do not support nanosecond precision for sleeping will have
/// `dur` rounded up to the nearest granularity of time they can sleep for.
///
-/// # Example
+/// # Examples
///
/// Waiting for the complete expiration of the timeout:
///
@@ -804,12 +869,30 @@ pub fn park_timeout_ms(ms: u32) {
#[stable(feature = "park_timeout", since = "1.4.0")]
pub fn park_timeout(dur: Duration) {
let thread = current();
- let mut guard = thread.inner.lock.lock().unwrap();
- if !*guard {
- let (g, _) = thread.inner.cvar.wait_timeout(guard, dur).unwrap();
- guard = g;
+
+ // Like `park` above we have a fast path for an already-notified thread, and
+ // afterwards we start coordinating for a sleep.
+ // return quickly.
+ if thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
+ return
+ }
+ let m = thread.inner.lock.lock().unwrap();
+ match thread.inner.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
+ Ok(_) => {}
+ Err(NOTIFIED) => return, // notified after we locked
+ Err(_) => panic!("inconsistent park_timeout state"),
+ }
+
+ // Wait with a timeout, and if we spuriously wake up or otherwise wake up
+ // from a notification we just want to unconditionally set the state back to
+ // empty, either consuming a notification or un-flagging ourselves as
+ // parked.
+ let (_m, _result) = thread.inner.cvar.wait_timeout(m, dur).unwrap();
+ match thread.inner.state.swap(EMPTY, SeqCst) {
+ NOTIFIED => {} // got a notification, hurray!
+ PARKED => {} // no notification, alas
+ n => panic!("inconsistent park_timeout state: {}", n),
}
- *guard = false;
}
////////////////////////////////////////////////////////////////////////////////
@@ -820,7 +903,8 @@ pub fn park_timeout(dur: Duration) {
///
/// A `ThreadId` is an opaque object that has a unique value for each thread
/// that creates one. `ThreadId`s are not guaranteed to correspond to a thread's
-/// system-designated identifier.
+/// system-designated identifier. A `ThreadId` can be retrieved from the [`id`]
+/// method on a [`Thread`].
///
/// # Examples
///
@@ -834,6 +918,9 @@ pub fn park_timeout(dur: Duration) {
/// let other_thread_id = other_thread.join().unwrap();
/// assert!(thread::current().id() != other_thread_id);
/// ```
+///
+/// [`id`]: ../../std/thread/struct.Thread.html#method.id
+/// [`Thread`]: ../../std/thread/struct.Thread.html
#[stable(feature = "thread_id", since = "1.19.0")]
#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)]
pub struct ThreadId(u64);
@@ -872,7 +959,10 @@ impl ThreadId {
struct Inner {
name: Option<CString>, // Guaranteed to be UTF-8
id: ThreadId,
- lock: Mutex<bool>, // true when there is a buffered unpark
+
+ // state for thread park/unpark
+ state: AtomicUsize,
+ lock: Mutex<()>,
cvar: Condvar,
}
@@ -896,6 +986,9 @@ struct Inner {
/// docs of [`Builder`] and [`spawn`] for more details.
///
/// [`Builder`]: ../../std/thread/struct.Builder.html
+/// [`JoinHandle::thread`]: ../../std/thread/struct.JoinHandle.html#method.thread
+/// [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html
+/// [`thread::current`]: ../../std/thread/fn.current.html
/// [`spawn`]: ../../std/thread/fn.spawn.html
pub struct Thread {
@@ -904,6 +997,7 @@ pub struct Thread {
impl Thread {
// Used only internally to construct a thread object without spawning
+ // Panics if the name contains nuls.
pub(crate) fn new(name: Option<String>) -> Thread {
let cname = name.map(|n| {
CString::new(n).expect("thread name may not contain interior null bytes")
@@ -912,7 +1006,8 @@ impl Thread {
inner: Arc::new(Inner {
name: cname,
id: ThreadId::new(),
- lock: Mutex::new(false),
+ state: AtomicUsize::new(EMPTY),
+ lock: Mutex::new(()),
cvar: Condvar::new(),
})
}
@@ -952,10 +1047,22 @@ impl Thread {
/// [park]: fn.park.html
#[stable(feature = "rust1", since = "1.0.0")]
pub fn unpark(&self) {
- let mut guard = self.inner.lock.lock().unwrap();
- if !*guard {
- *guard = true;
- self.inner.cvar.notify_one();
+ loop {
+ match self.inner.state.compare_exchange(EMPTY, NOTIFIED, SeqCst, SeqCst) {
+ Ok(_) => return, // no one was waiting
+ Err(NOTIFIED) => return, // already unparked
+ Err(PARKED) => {} // gotta go wake someone up
+ _ => panic!("inconsistent state in unpark"),
+ }
+
+ // Coordinate wakeup through the mutex and a condvar notification
+ let _lock = self.inner.lock.lock().unwrap();
+ match self.inner.state.compare_exchange(PARKED, NOTIFIED, SeqCst, SeqCst) {
+ Ok(_) => return self.inner.cvar.notify_one(),
+ Err(NOTIFIED) => return, // a different thread unparked
+ Err(EMPTY) => {} // parked thread went away, try again
+ _ => panic!("inconsistent state in unpark"),
+ }
}
}
@@ -980,6 +1087,9 @@ impl Thread {
/// Gets the thread's name.
///
+ /// For more information about named threads, see
+ /// [this module-level documentation][naming-threads].
+ ///
/// # Examples
///
/// Threads by default have no name specified:
@@ -1010,6 +1120,8 @@ impl Thread {
///
/// handler.join().unwrap();
/// ```
+ ///
+ /// [naming-threads]: ./index.html#naming-threads
#[stable(feature = "rust1", since = "1.0.0")]
pub fn name(&self) -> Option<&str> {
self.cname().map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) } )
@@ -1143,7 +1255,7 @@ impl<T> JoinInner<T> {
/// });
/// });
///
-/// let _ = original_thread.join();
+/// original_thread.join().expect("The thread being joined has panicked");
/// println!("Original thread is joined.");
///
/// // We make sure that the new thread has time to run, before the main
@@ -1188,6 +1300,11 @@ impl<T> JoinHandle<T> {
/// [`Err`]: ../../std/result/enum.Result.html#variant.Err
/// [`panic`]: ../../std/macro.panic.html
///
+ /// # Panics
+ ///
+ /// This function may panic on some platforms if a thread attempts to join
+ /// itself or otherwise may create a deadlock with joining threads.
+ ///
/// # Examples
///
/// ```