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
|
@ -16,7 +16,7 @@
|
|||
//! each with their own stack and local state.
|
||||
//!
|
||||
//! 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
|
||||
//! synchronization](../../std/sync/index.html) and shared-memory data
|
||||
//! structures. In particular, types that are guaranteed to be
|
||||
|
@ -58,25 +58,45 @@
|
|||
//! ```rust
|
||||
//! use std::thread::Thread;
|
||||
//!
|
||||
//! let guard = Thread::spawn(move || {
|
||||
//! let thread = Thread::spawn(move || {
|
||||
//! println!("Hello, World!");
|
||||
//! // 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();
|
||||
//! ```
|
||||
//!
|
||||
//! The `spawn` function doesn't return a `Thread` directly; instead, it returns
|
||||
//! a *join guard* from which a `Thread` can be extracted. The join guard is an
|
||||
//! RAII-style guard that will automatically join the child thread (block until
|
||||
//! it terminates) when it is dropped. You can join the child thread in advance
|
||||
//! by calling the `join` method on the guard, which will also return the result
|
||||
//! produced by the thread.
|
||||
//! The `scoped` function doesn't return a `Thread` directly; instead, it
|
||||
//! returns a *join guard* from which a `Thread` can be extracted. The join
|
||||
//! guard is an RAII-style guard that will automatically join the child thread
|
||||
//! (block until it terminates) when it is dropped. You can join the child
|
||||
//! thread in advance by calling the `join` method on the guard, which will also
|
||||
//! 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
|
||||
//! parent, you can use the `detach` method on the guard,
|
||||
//!
|
||||
//! A handle to the thread itself is available via the `thread` method on the
|
||||
//! join guard.
|
||||
//! (Note: eventually, the `scoped` constructor will allow the parent and child
|
||||
//! threads to data that lives on the parent thread's stack, but some language
|
||||
//! changes are needed before this is possible.)
|
||||
//!
|
||||
//! ## Configuring threads
|
||||
//!
|
||||
|
@ -89,7 +109,7 @@
|
|||
//!
|
||||
//! thread::Builder::new().name("child1".to_string()).spawn(move || {
|
||||
//! println!("Hello, world!")
|
||||
//! }).detach();
|
||||
//! });
|
||||
//! ```
|
||||
//!
|
||||
//! ## Blocking support: park and unpark
|
||||
|
@ -124,6 +144,8 @@
|
|||
//!
|
||||
//! * It can be implemented highly efficiently on many platforms.
|
||||
|
||||
#![stable]
|
||||
|
||||
use any::Any;
|
||||
use boxed::Box;
|
||||
use cell::UnsafeCell;
|
||||
|
@ -144,6 +166,7 @@ use sys_common::{stack, thread_info};
|
|||
|
||||
/// Thread configuation. Provides detailed control over the properties
|
||||
/// and behavior of new threads.
|
||||
#[stable]
|
||||
pub struct Builder {
|
||||
// A name for the thread-to-be, for identification in panic messages
|
||||
name: Option<String>,
|
||||
|
@ -158,6 +181,7 @@ pub struct Builder {
|
|||
impl Builder {
|
||||
/// Generate the base configuration for spawning a thread, from which
|
||||
/// configuration methods can be chained.
|
||||
#[stable]
|
||||
pub fn new() -> Builder {
|
||||
Builder {
|
||||
name: None,
|
||||
|
@ -169,12 +193,14 @@ impl Builder {
|
|||
|
||||
/// Name the thread-to-be. Currently the name is used for identification
|
||||
/// only in panic messages.
|
||||
#[stable]
|
||||
pub fn name(mut self, name: String) -> Builder {
|
||||
self.name = Some(name);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the size of the stack for the new thread.
|
||||
#[stable]
|
||||
pub fn stack_size(mut self, size: uint) -> Builder {
|
||||
self.stack_size = Some(size);
|
||||
self
|
||||
|
@ -194,19 +220,41 @@ impl Builder {
|
|||
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.
|
||||
pub fn spawn<T, F>(self, f: F) -> JoinGuard<T> where
|
||||
T: Send, F: FnOnce() -> T, F: Send
|
||||
{
|
||||
self.spawn_inner(Thunk::new(f))
|
||||
#[unstable = "may change with specifics of new Send semantics"]
|
||||
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(|_| {}));
|
||||
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 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 stack_size = stack_size.unwrap_or(rt::min_stack());
|
||||
|
@ -258,21 +306,14 @@ impl Builder {
|
|||
unwind::try(move || *ptr = Some(f.invoke(())))
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
*their_packet.0.get() = Some(match (output, try_result) {
|
||||
finish.invoke(match (output, try_result) {
|
||||
(Some(data), Ok(_)) => Ok(data),
|
||||
(None, Err(cause)) => Err(cause),
|
||||
_ => unreachable!()
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
JoinGuard {
|
||||
native: unsafe { imp::create(stack_size, Thunk::new(main)) },
|
||||
joined: false,
|
||||
packet: my_packet,
|
||||
thread: my_thread,
|
||||
}
|
||||
(unsafe { imp::create(stack_size, Thunk::new(main)) }, my_thread)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -285,11 +326,13 @@ struct Inner {
|
|||
unsafe impl Sync for Inner {}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[stable]
|
||||
/// A handle to a thread.
|
||||
pub struct Thread {
|
||||
inner: Arc<Inner>,
|
||||
}
|
||||
|
||||
#[stable]
|
||||
unsafe impl Sync for 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
|
||||
/// `join`), returning `Result<T>`, or it will implicitly join the child
|
||||
/// upon being dropped. To detach the child, allowing it to outlive the
|
||||
/// current thread, use `detach`. See the module documentation for additional details.
|
||||
pub fn spawn<T, F>(f: F) -> JoinGuard<T> where
|
||||
T: Send, F: FnOnce() -> T, F: Send
|
||||
{
|
||||
/// The child thread may outlive the parent (unless the parent thread is the
|
||||
/// main thread; the whole process is terminated when the main thread
|
||||
/// finishes.) The thread handle can be used for low-level
|
||||
/// synchronization. See the module documentation for additional details.
|
||||
#[unstable = "may change with specifics of new Send semantics"]
|
||||
pub fn spawn<F>(f: F) -> Thread where F: FnOnce(), F: Send + 'static {
|
||||
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.
|
||||
#[stable]
|
||||
pub fn current() -> Thread {
|
||||
thread_info::current_thread()
|
||||
}
|
||||
|
||||
/// Cooperatively give up a timeslice to the OS scheduler.
|
||||
#[unstable = "name may change"]
|
||||
pub fn yield_now() {
|
||||
unsafe { imp::yield_now() }
|
||||
}
|
||||
|
||||
/// Determines whether the current thread is panicking.
|
||||
#[inline]
|
||||
#[stable]
|
||||
pub fn panicking() -> bool {
|
||||
unwind::panicking()
|
||||
}
|
||||
|
@ -341,6 +401,7 @@ impl Thread {
|
|||
// 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
|
||||
// or futuxes, and in either case may allow spurious wakeups.
|
||||
#[unstable = "recently introduced"]
|
||||
pub fn park() {
|
||||
let thread = Thread::current();
|
||||
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.
|
||||
///
|
||||
/// See the module doc for more detail.
|
||||
#[unstable = "recently introduced"]
|
||||
pub fn unpark(&self) {
|
||||
let mut guard = self.inner.lock.lock().unwrap();
|
||||
if !*guard {
|
||||
|
@ -362,6 +424,7 @@ impl Thread {
|
|||
}
|
||||
|
||||
/// Get the thread's name.
|
||||
#[stable]
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
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.
|
||||
///
|
||||
/// A thread that completes without panicking is considered to exit successfully.
|
||||
#[stable]
|
||||
pub type Result<T> = ::result::Result<T, Box<Any + Send>>;
|
||||
|
||||
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> Sync for Packet<T> {}
|
||||
|
||||
#[must_use]
|
||||
/// 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.
|
||||
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,
|
||||
thread: Thread,
|
||||
joined: bool,
|
||||
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.
|
||||
#[stable]
|
||||
pub fn 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
|
||||
/// to `panic`.
|
||||
#[stable]
|
||||
pub fn join(mut self) -> Result<T> {
|
||||
assert!(!self.joined);
|
||||
unsafe { imp::join(self.native) };
|
||||
|
@ -414,8 +482,11 @@ impl<T: Send> JoinGuard<T> {
|
|||
(*self.packet.0.get()).take().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send> JoinGuard<'static, T> {
|
||||
/// Detaches the child thread, allowing it to outlive its parent.
|
||||
#[experimental = "unsure whether this API imposes limitations elsewhere"]
|
||||
pub fn detach(mut self) {
|
||||
unsafe { imp::detach(self.native) };
|
||||
self.joined = true; // avoid joining in the destructor
|
||||
|
@ -424,7 +495,7 @@ impl<T: Send> JoinGuard<T> {
|
|||
|
||||
#[unsafe_destructor]
|
||||
#[stable]
|
||||
impl<T: Send> Drop for JoinGuard<T> {
|
||||
impl<'a, T: Send + 'a> Drop for JoinGuard<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
if !self.joined {
|
||||
unsafe { imp::join(self.native) };
|
||||
|
|
Loading…
Reference in New Issue