Stabilize std::thread
This commit takes a first pass at stabilizing `std::thread`: * It removes the `detach` method in favor of two constructors -- `spawn` for detached threads, `scoped` for "scoped" (i.e., must-join) threads. This addresses some of the surprise/frustrating debug sessions with the previous API, in which `spawn` produced a guard that on destruction joined the thread (unless `detach` was called). The reason to have the division in part is that `Send` will soon not imply `'static`, which means that `scoped` thread creation can take a closure over *shared stack data* of the parent thread. On the other hand, this means that the parent must not pop the relevant stack frames while the child thread is running. The `JoinGuard` is used to prevent this from happening by joining on drop (if you have not already explicitly `join`ed.) The APIs around `scoped` are future-proofed for the `Send` changes by taking an additional lifetime parameter. With the current definition of `Send`, this is forced to be `'static`, but when `Send` changes these APIs will gain their full flexibility immediately. Threads that are `spawn`ed, on the other hand, are detached from the start and do not yield an RAII guard. The hope is that, by making `scoped` an explicit opt-in with a very suggestive name, it will be drastically less likely to be caught by a surprising deadlock due to an implicit join at the end of a scope. * The module itself is marked stable. * Existing methods other than `spawn` and `scoped` are marked stable. The migration path is: ```rust Thread::spawn(f).detached() ``` becomes ```rust Thread::spawn(f) ``` while ```rust let res = Thread::spawn(f); res.join() ``` becomes ```rust let res = Thread::scoped(f); res.join() ``` [breaking-change]
This commit is contained in:
parent
8efd9901b6
commit
f67b81e8d4
1 changed files with 117 additions and 46 deletions
|
@ -16,7 +16,7 @@
|
||||||
//! each with their own stack and local state.
|
//! each with their own stack and local state.
|
||||||
//!
|
//!
|
||||||
//! Communication between threads can be done through
|
//! Communication between threads can be done through
|
||||||
//! [channels](../../std/comm/index.html), Rust's message-passing
|
//! [channels](../../std/sync/mpsc/index.html), Rust's message-passing
|
||||||
//! types, along with [other forms of thread
|
//! types, along with [other forms of thread
|
||||||
//! synchronization](../../std/sync/index.html) and shared-memory data
|
//! synchronization](../../std/sync/index.html) and shared-memory data
|
||||||
//! structures. In particular, types that are guaranteed to be
|
//! structures. In particular, types that are guaranteed to be
|
||||||
|
@ -58,25 +58,45 @@
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use std::thread::Thread;
|
//! use std::thread::Thread;
|
||||||
//!
|
//!
|
||||||
//! let guard = Thread::spawn(move || {
|
//! let thread = Thread::spawn(move || {
|
||||||
//! println!("Hello, World!");
|
//! println!("Hello, World!");
|
||||||
//! // some computation here
|
//! // some computation here
|
||||||
//! });
|
//! });
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! The spawned thread is "detached" from the current thread, meaning that it
|
||||||
|
//! can outlive the thread that spawned it. (Note, however, that when the main
|
||||||
|
//! thread terminates all detached threads are terminated as well.) The returned
|
||||||
|
//! `Thread` handle can be used for low-level synchronization as described below.
|
||||||
|
//!
|
||||||
|
//! ## Scoped threads
|
||||||
|
//!
|
||||||
|
//! Often a parent thread uses a child thread to perform some particular task,
|
||||||
|
//! and at some point must wait for the child to complete before continuing.
|
||||||
|
//! For this scenario, use the `scoped` constructor:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use std::thread::Thread;
|
||||||
|
//!
|
||||||
|
//! let guard = Thread::scoped(move || {
|
||||||
|
//! println!("Hello, World!");
|
||||||
|
//! // some computation here
|
||||||
|
//! });
|
||||||
|
//! // do some other work in the meantime
|
||||||
//! let result = guard.join();
|
//! let result = guard.join();
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! The `spawn` function doesn't return a `Thread` directly; instead, it returns
|
//! The `scoped` function doesn't return a `Thread` directly; instead, it
|
||||||
//! a *join guard* from which a `Thread` can be extracted. The join guard is an
|
//! returns a *join guard* from which a `Thread` can be extracted. The join
|
||||||
//! RAII-style guard that will automatically join the child thread (block until
|
//! guard is an RAII-style guard that will automatically join the child thread
|
||||||
//! it terminates) when it is dropped. You can join the child thread in advance
|
//! (block until it terminates) when it is dropped. You can join the child
|
||||||
//! by calling the `join` method on the guard, which will also return the result
|
//! thread in advance by calling the `join` method on the guard, which will also
|
||||||
//! produced by the thread.
|
//! return the result produced by the thread. A handle to the thread itself is
|
||||||
|
//! available via the `thread` method on the join guard.
|
||||||
//!
|
//!
|
||||||
//! If you instead wish to *detach* the child thread, allowing it to outlive its
|
//! (Note: eventually, the `scoped` constructor will allow the parent and child
|
||||||
//! parent, you can use the `detach` method on the guard,
|
//! threads to data that lives on the parent thread's stack, but some language
|
||||||
//!
|
//! changes are needed before this is possible.)
|
||||||
//! A handle to the thread itself is available via the `thread` method on the
|
|
||||||
//! join guard.
|
|
||||||
//!
|
//!
|
||||||
//! ## Configuring threads
|
//! ## Configuring threads
|
||||||
//!
|
//!
|
||||||
|
@ -89,7 +109,7 @@
|
||||||
//!
|
//!
|
||||||
//! thread::Builder::new().name("child1".to_string()).spawn(move || {
|
//! thread::Builder::new().name("child1".to_string()).spawn(move || {
|
||||||
//! println!("Hello, world!")
|
//! println!("Hello, world!")
|
||||||
//! }).detach();
|
//! });
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ## Blocking support: park and unpark
|
//! ## Blocking support: park and unpark
|
||||||
|
@ -124,6 +144,8 @@
|
||||||
//!
|
//!
|
||||||
//! * It can be implemented highly efficiently on many platforms.
|
//! * It can be implemented highly efficiently on many platforms.
|
||||||
|
|
||||||
|
#![stable]
|
||||||
|
|
||||||
use any::Any;
|
use any::Any;
|
||||||
use boxed::Box;
|
use boxed::Box;
|
||||||
use cell::UnsafeCell;
|
use cell::UnsafeCell;
|
||||||
|
@ -144,6 +166,7 @@ use sys_common::{stack, thread_info};
|
||||||
|
|
||||||
/// Thread configuation. Provides detailed control over the properties
|
/// Thread configuation. Provides detailed control over the properties
|
||||||
/// and behavior of new threads.
|
/// and behavior of new threads.
|
||||||
|
#[stable]
|
||||||
pub struct Builder {
|
pub struct Builder {
|
||||||
// A name for the thread-to-be, for identification in panic messages
|
// A name for the thread-to-be, for identification in panic messages
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
|
@ -158,6 +181,7 @@ pub struct Builder {
|
||||||
impl Builder {
|
impl Builder {
|
||||||
/// Generate the base configuration for spawning a thread, from which
|
/// Generate the base configuration for spawning a thread, from which
|
||||||
/// configuration methods can be chained.
|
/// configuration methods can be chained.
|
||||||
|
#[stable]
|
||||||
pub fn new() -> Builder {
|
pub fn new() -> Builder {
|
||||||
Builder {
|
Builder {
|
||||||
name: None,
|
name: None,
|
||||||
|
@ -169,12 +193,14 @@ impl Builder {
|
||||||
|
|
||||||
/// Name the thread-to-be. Currently the name is used for identification
|
/// Name the thread-to-be. Currently the name is used for identification
|
||||||
/// only in panic messages.
|
/// only in panic messages.
|
||||||
|
#[stable]
|
||||||
pub fn name(mut self, name: String) -> Builder {
|
pub fn name(mut self, name: String) -> Builder {
|
||||||
self.name = Some(name);
|
self.name = Some(name);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the size of the stack for the new thread.
|
/// Set the size of the stack for the new thread.
|
||||||
|
#[stable]
|
||||||
pub fn stack_size(mut self, size: uint) -> Builder {
|
pub fn stack_size(mut self, size: uint) -> Builder {
|
||||||
self.stack_size = Some(size);
|
self.stack_size = Some(size);
|
||||||
self
|
self
|
||||||
|
@ -194,19 +220,41 @@ impl Builder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawn a new joinable thread, and return a JoinGuard guard for it.
|
/// Spawn a new detached thread, and return a handle to it.
|
||||||
///
|
///
|
||||||
/// See `Thead::spawn` and the module doc for more details.
|
/// See `Thead::spawn` and the module doc for more details.
|
||||||
pub fn spawn<T, F>(self, f: F) -> JoinGuard<T> where
|
#[unstable = "may change with specifics of new Send semantics"]
|
||||||
T: Send, F: FnOnce() -> T, F: Send
|
pub fn spawn<F>(self, f: F) -> Thread where F: FnOnce(), F: Send + 'static {
|
||||||
{
|
let (native, thread) = self.spawn_inner(Thunk::new(f), Thunk::with_arg(|_| {}));
|
||||||
self.spawn_inner(Thunk::new(f))
|
unsafe { imp::detach(native) };
|
||||||
|
thread
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_inner<T: Send>(self, f: Thunk<(), T>) -> JoinGuard<T> {
|
/// Spawn a new child thread that must be joined within a given
|
||||||
|
/// scope, and return a `JoinGuard`.
|
||||||
|
///
|
||||||
|
/// See `Thead::scoped` and the module doc for more details.
|
||||||
|
#[unstable = "may change with specifics of new Send semantics"]
|
||||||
|
pub fn scoped<'a, T, F>(self, f: F) -> JoinGuard<'a, T> where
|
||||||
|
T: Send + 'a, F: FnOnce() -> T, F: Send + 'a
|
||||||
|
{
|
||||||
let my_packet = Packet(Arc::new(UnsafeCell::new(None)));
|
let my_packet = Packet(Arc::new(UnsafeCell::new(None)));
|
||||||
let their_packet = Packet(my_packet.0.clone());
|
let their_packet = Packet(my_packet.0.clone());
|
||||||
|
let (native, thread) = self.spawn_inner(Thunk::new(f), Thunk::with_arg(move |: ret| unsafe {
|
||||||
|
*their_packet.0.get() = Some(ret);
|
||||||
|
}));
|
||||||
|
|
||||||
|
JoinGuard {
|
||||||
|
native: native,
|
||||||
|
joined: false,
|
||||||
|
packet: my_packet,
|
||||||
|
thread: thread,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_inner<T: Send>(self, f: Thunk<(), T>, finish: Thunk<Result<T>, ()>)
|
||||||
|
-> (imp::rust_thread, Thread)
|
||||||
|
{
|
||||||
let Builder { name, stack_size, stdout, stderr } = self;
|
let Builder { name, stack_size, stdout, stderr } = self;
|
||||||
|
|
||||||
let stack_size = stack_size.unwrap_or(rt::min_stack());
|
let stack_size = stack_size.unwrap_or(rt::min_stack());
|
||||||
|
@ -258,21 +306,14 @@ impl Builder {
|
||||||
unwind::try(move || *ptr = Some(f.invoke(())))
|
unwind::try(move || *ptr = Some(f.invoke(())))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
unsafe {
|
finish.invoke(match (output, try_result) {
|
||||||
*their_packet.0.get() = Some(match (output, try_result) {
|
(Some(data), Ok(_)) => Ok(data),
|
||||||
(Some(data), Ok(_)) => Ok(data),
|
(None, Err(cause)) => Err(cause),
|
||||||
(None, Err(cause)) => Err(cause),
|
_ => unreachable!()
|
||||||
_ => unreachable!()
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
JoinGuard {
|
(unsafe { imp::create(stack_size, Thunk::new(main)) }, my_thread)
|
||||||
native: unsafe { imp::create(stack_size, Thunk::new(main)) },
|
|
||||||
joined: false,
|
|
||||||
packet: my_packet,
|
|
||||||
thread: my_thread,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,11 +326,13 @@ struct Inner {
|
||||||
unsafe impl Sync for Inner {}
|
unsafe impl Sync for Inner {}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
#[stable]
|
||||||
/// A handle to a thread.
|
/// A handle to a thread.
|
||||||
pub struct Thread {
|
pub struct Thread {
|
||||||
inner: Arc<Inner>,
|
inner: Arc<Inner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[stable]
|
||||||
unsafe impl Sync for Thread {}
|
unsafe impl Sync for Thread {}
|
||||||
|
|
||||||
impl Thread {
|
impl Thread {
|
||||||
|
@ -304,30 +347,47 @@ impl Thread {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawn a new joinable thread, returning a `JoinGuard` for it.
|
/// Spawn a new detached thread, returning a handle to it.
|
||||||
///
|
///
|
||||||
/// The join guard can be used to explicitly join the child thread (via
|
/// The child thread may outlive the parent (unless the parent thread is the
|
||||||
/// `join`), returning `Result<T>`, or it will implicitly join the child
|
/// main thread; the whole process is terminated when the main thread
|
||||||
/// upon being dropped. To detach the child, allowing it to outlive the
|
/// finishes.) The thread handle can be used for low-level
|
||||||
/// current thread, use `detach`. See the module documentation for additional details.
|
/// synchronization. See the module documentation for additional details.
|
||||||
pub fn spawn<T, F>(f: F) -> JoinGuard<T> where
|
#[unstable = "may change with specifics of new Send semantics"]
|
||||||
T: Send, F: FnOnce() -> T, F: Send
|
pub fn spawn<F>(f: F) -> Thread where F: FnOnce(), F: Send + 'static {
|
||||||
{
|
|
||||||
Builder::new().spawn(f)
|
Builder::new().spawn(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Spawn a new *scoped* thread, returning a `JoinGuard` for it.
|
||||||
|
///
|
||||||
|
/// The join guard can be used to explicitly join the child thread (via
|
||||||
|
/// `join`), returning `Result<T>`, or it will implicitly join the child
|
||||||
|
/// upon being dropped. Because the child thread may refer to data on the
|
||||||
|
/// current thread's stack (hence the "scoped" name), it cannot be detached;
|
||||||
|
/// it *must* be joined before the relevant stack frame is popped. See the
|
||||||
|
/// module documentation for additional details.
|
||||||
|
#[unstable = "may change with specifics of new Send semantics"]
|
||||||
|
pub fn scoped<'a, T, F>(f: F) -> JoinGuard<'a, T> where
|
||||||
|
T: Send + 'a, F: FnOnce() -> T, F: Send + 'a
|
||||||
|
{
|
||||||
|
Builder::new().scoped(f)
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets a handle to the thread that invokes it.
|
/// Gets a handle to the thread that invokes it.
|
||||||
|
#[stable]
|
||||||
pub fn current() -> Thread {
|
pub fn current() -> Thread {
|
||||||
thread_info::current_thread()
|
thread_info::current_thread()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cooperatively give up a timeslice to the OS scheduler.
|
/// Cooperatively give up a timeslice to the OS scheduler.
|
||||||
|
#[unstable = "name may change"]
|
||||||
pub fn yield_now() {
|
pub fn yield_now() {
|
||||||
unsafe { imp::yield_now() }
|
unsafe { imp::yield_now() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines whether the current thread is panicking.
|
/// Determines whether the current thread is panicking.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[stable]
|
||||||
pub fn panicking() -> bool {
|
pub fn panicking() -> bool {
|
||||||
unwind::panicking()
|
unwind::panicking()
|
||||||
}
|
}
|
||||||
|
@ -341,6 +401,7 @@ impl Thread {
|
||||||
// future, this will be implemented in a more efficient way, perhaps along the lines of
|
// future, this will be implemented in a more efficient way, perhaps along the lines of
|
||||||
// http://cr.openjdk.java.net/~stefank/6989984.1/raw_files/new/src/os/linux/vm/os_linux.cpp
|
// http://cr.openjdk.java.net/~stefank/6989984.1/raw_files/new/src/os/linux/vm/os_linux.cpp
|
||||||
// or futuxes, and in either case may allow spurious wakeups.
|
// or futuxes, and in either case may allow spurious wakeups.
|
||||||
|
#[unstable = "recently introduced"]
|
||||||
pub fn park() {
|
pub fn park() {
|
||||||
let thread = Thread::current();
|
let thread = Thread::current();
|
||||||
let mut guard = thread.inner.lock.lock().unwrap();
|
let mut guard = thread.inner.lock.lock().unwrap();
|
||||||
|
@ -353,6 +414,7 @@ impl Thread {
|
||||||
/// Atomically makes the handle's token available if it is not already.
|
/// Atomically makes the handle's token available if it is not already.
|
||||||
///
|
///
|
||||||
/// See the module doc for more detail.
|
/// See the module doc for more detail.
|
||||||
|
#[unstable = "recently introduced"]
|
||||||
pub fn unpark(&self) {
|
pub fn unpark(&self) {
|
||||||
let mut guard = self.inner.lock.lock().unwrap();
|
let mut guard = self.inner.lock.lock().unwrap();
|
||||||
if !*guard {
|
if !*guard {
|
||||||
|
@ -362,6 +424,7 @@ impl Thread {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the thread's name.
|
/// Get the thread's name.
|
||||||
|
#[stable]
|
||||||
pub fn name(&self) -> Option<&str> {
|
pub fn name(&self) -> Option<&str> {
|
||||||
self.inner.name.as_ref().map(|s| s.as_slice())
|
self.inner.name.as_ref().map(|s| s.as_slice())
|
||||||
}
|
}
|
||||||
|
@ -375,6 +438,7 @@ impl thread_info::NewThread for Thread {
|
||||||
/// Indicates the manner in which a thread exited.
|
/// Indicates the manner in which a thread exited.
|
||||||
///
|
///
|
||||||
/// A thread that completes without panicking is considered to exit successfully.
|
/// A thread that completes without panicking is considered to exit successfully.
|
||||||
|
#[stable]
|
||||||
pub type Result<T> = ::result::Result<T, Box<Any + Send>>;
|
pub type Result<T> = ::result::Result<T, Box<Any + Send>>;
|
||||||
|
|
||||||
struct Packet<T>(Arc<UnsafeCell<Option<Result<T>>>>);
|
struct Packet<T>(Arc<UnsafeCell<Option<Result<T>>>>);
|
||||||
|
@ -382,21 +446,24 @@ struct Packet<T>(Arc<UnsafeCell<Option<Result<T>>>>);
|
||||||
unsafe impl<T:'static+Send> Send for Packet<T> {}
|
unsafe impl<T:'static+Send> Send for Packet<T> {}
|
||||||
unsafe impl<T> Sync for Packet<T> {}
|
unsafe impl<T> Sync for Packet<T> {}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// An RAII-style guard that will block until thread termination when dropped.
|
/// An RAII-style guard that will block until thread termination when dropped.
|
||||||
///
|
///
|
||||||
/// The type `T` is the return type for the thread's main function.
|
/// The type `T` is the return type for the thread's main function.
|
||||||
pub struct JoinGuard<T> {
|
#[must_use]
|
||||||
|
#[unstable = "may change with specifics of new Send semantics"]
|
||||||
|
pub struct JoinGuard<'a, T: 'a> {
|
||||||
native: imp::rust_thread,
|
native: imp::rust_thread,
|
||||||
thread: Thread,
|
thread: Thread,
|
||||||
joined: bool,
|
joined: bool,
|
||||||
packet: Packet<T>,
|
packet: Packet<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: Send> Sync for JoinGuard<T> {}
|
#[stable]
|
||||||
|
unsafe impl<'a, T: Send + 'a> Sync for JoinGuard<'a, T> {}
|
||||||
|
|
||||||
impl<T: Send> JoinGuard<T> {
|
impl<'a, T: Send + 'a> JoinGuard<'a, T> {
|
||||||
/// Extract a handle to the thread this guard will join on.
|
/// Extract a handle to the thread this guard will join on.
|
||||||
|
#[stable]
|
||||||
pub fn thread(&self) -> &Thread {
|
pub fn thread(&self) -> &Thread {
|
||||||
&self.thread
|
&self.thread
|
||||||
}
|
}
|
||||||
|
@ -406,6 +473,7 @@ impl<T: Send> JoinGuard<T> {
|
||||||
///
|
///
|
||||||
/// If the child thread panics, `Err` is returned with the parameter given
|
/// If the child thread panics, `Err` is returned with the parameter given
|
||||||
/// to `panic`.
|
/// to `panic`.
|
||||||
|
#[stable]
|
||||||
pub fn join(mut self) -> Result<T> {
|
pub fn join(mut self) -> Result<T> {
|
||||||
assert!(!self.joined);
|
assert!(!self.joined);
|
||||||
unsafe { imp::join(self.native) };
|
unsafe { imp::join(self.native) };
|
||||||
|
@ -414,8 +482,11 @@ impl<T: Send> JoinGuard<T> {
|
||||||
(*self.packet.0.get()).take().unwrap()
|
(*self.packet.0.get()).take().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send> JoinGuard<'static, T> {
|
||||||
/// Detaches the child thread, allowing it to outlive its parent.
|
/// Detaches the child thread, allowing it to outlive its parent.
|
||||||
|
#[experimental = "unsure whether this API imposes limitations elsewhere"]
|
||||||
pub fn detach(mut self) {
|
pub fn detach(mut self) {
|
||||||
unsafe { imp::detach(self.native) };
|
unsafe { imp::detach(self.native) };
|
||||||
self.joined = true; // avoid joining in the destructor
|
self.joined = true; // avoid joining in the destructor
|
||||||
|
@ -424,7 +495,7 @@ impl<T: Send> JoinGuard<T> {
|
||||||
|
|
||||||
#[unsafe_destructor]
|
#[unsafe_destructor]
|
||||||
#[stable]
|
#[stable]
|
||||||
impl<T: Send> Drop for JoinGuard<T> {
|
impl<'a, T: Send + 'a> Drop for JoinGuard<'a, T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if !self.joined {
|
if !self.joined {
|
||||||
unsafe { imp::join(self.native) };
|
unsafe { imp::join(self.native) };
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue