From f67b81e8d4dc198ad10ad50a7624e43cc1e25802 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 5 Jan 2015 15:45:18 -0800 Subject: [PATCH] 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] --- src/libstd/thread.rs | 163 +++++++++++++++++++++++++++++++------------ 1 file changed, 117 insertions(+), 46 deletions(-) diff --git a/src/libstd/thread.rs b/src/libstd/thread.rs index cc82d38ae2a..9e12a7b592a 100644 --- a/src/libstd/thread.rs +++ b/src/libstd/thread.rs @@ -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, @@ -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(self, f: F) -> JoinGuard 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(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(self, f: Thunk<(), T>) -> JoinGuard { + /// 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(self, f: Thunk<(), T>, finish: Thunk, ()>) + -> (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) { - (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) } } @@ -285,11 +326,13 @@ struct Inner { unsafe impl Sync for Inner {} #[derive(Clone)] +#[stable] /// A handle to a thread. pub struct Thread { inner: Arc, } +#[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`, 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(f: F) -> JoinGuard 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) -> 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`, 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 = ::result::Result>; struct Packet(Arc>>>); @@ -382,21 +446,24 @@ struct Packet(Arc>>>); unsafe impl Send for Packet {} unsafe impl Sync for Packet {} -#[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 { +#[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, } -unsafe impl Sync for JoinGuard {} +#[stable] +unsafe impl<'a, T: Send + 'a> Sync for JoinGuard<'a, T> {} -impl JoinGuard { +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 JoinGuard { /// /// If the child thread panics, `Err` is returned with the parameter given /// to `panic`. + #[stable] pub fn join(mut self) -> Result { assert!(!self.joined); unsafe { imp::join(self.native) }; @@ -414,8 +482,11 @@ impl JoinGuard { (*self.packet.0.get()).take().unwrap() } } +} +impl 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 JoinGuard { #[unsafe_destructor] #[stable] -impl Drop for JoinGuard { +impl<'a, T: Send + 'a> Drop for JoinGuard<'a, T> { fn drop(&mut self) { if !self.joined { unsafe { imp::join(self.native) };