Skip to content

Commit

Permalink
Stabilize std::thread
Browse files Browse the repository at this point in the history
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]
  • Loading branch information
aturon committed Jan 6, 2015
1 parent 8efd990 commit f67b81e
Showing 1 changed file with 115 additions and 44 deletions.
159 changes: 115 additions & 44 deletions src/libstd/thread.rs
Expand Up @@ -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
Expand Down Expand Up @@ -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
//! });
//! 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 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();
//! ```
//!
//! If you instead wish to *detach* the child thread, allowing it to outlive its
//! parent, you can use the `detach` method on the guard,
//! 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.
//!
//! 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
//!
Expand All @@ -89,7 +109,7 @@
//!
//! thread::Builder::new().name("child1".to_string()).spawn(move || {
//! println!("Hello, world!")
//! }).detach();
//! });
//! ```
//!
//! ## Blocking support: park and unpark
Expand Down Expand Up @@ -124,6 +144,8 @@
//!
//! * It can be implemented highly efficiently on many platforms.

#![stable]

use any::Any;
use boxed::Box;
use cell::UnsafeCell;
Expand All @@ -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>,
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -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());
Expand Down Expand Up @@ -258,21 +306,14 @@ impl Builder {
unwind::try(move || *ptr = Some(f.invoke(())))
}
};
unsafe {
*their_packet.0.get() = Some(match (output, try_result) {
(Some(data), Ok(_)) => Ok(data),
(None, Err(cause)) => Err(cause),
_ => unreachable!()
});
}
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)
}
}

Expand All @@ -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 {
Expand All @@ -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 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. 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
/// 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().spawn(f)
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()
}
Expand All @@ -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();
Expand All @@ -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 {
Expand All @@ -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())
}
Expand All @@ -375,28 +438,32 @@ 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>>>>);

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
}
Expand All @@ -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) };
Expand All @@ -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
Expand All @@ -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) };
Expand Down

0 comments on commit f67b81e

Please sign in to comment.