Revamp TaskBuilder API

This patch consolidates and cleans up the task spawning APIs:

* Removes the problematic `future_result` method from `std::task::TaskBuilder`,
  and adds a `try_future` that both spawns the task and returns a future
  representing its eventual result (or failure).

* Removes the public `opts` field from `TaskBuilder`, instead adding appropriate
  builder methods to configure the task.

* Adds extension traits to libgreen and libnative that add methods to
  `TaskBuilder` for spawning the task as a green or native thread.

Previously, there was no way to benefit from the `TaskBuilder` functionality and
also set the scheduler to spawn within.

With this change, all task spawning scenarios are supported through the
`TaskBuilder` interface.

Closes #3725.

[breaking-change]
This commit is contained in:
Aaron Turon 2014-06-17 14:48:54 -07:00 committed by Alex Crichton
parent 8e9e17d188
commit a23511a65d
5 changed files with 619 additions and 420 deletions

View file

@ -159,16 +159,19 @@
//!
//! # Using a scheduler pool
//!
//! This library adds a `GreenTaskBuilder` trait that extends the methods
//! available on `std::task::TaskBuilder` to allow spawning a green task,
//! possibly pinned to a particular scheduler thread:
//!
//! ```rust
//! use std::rt::task::TaskOpts;
//! use green::{SchedPool, PoolConfig};
//! use green::sched::{PinnedTask, TaskFromFriend};
//! use std::task::TaskBuilder;
//! use green::{SchedPool, PoolConfig, GreenTaskBuilder};
//!
//! let config = PoolConfig::new();
//! let mut pool = SchedPool::new(config);
//!
//! // Spawn tasks into the pool of schedulers
//! pool.spawn(TaskOpts::new(), proc() {
//! TaskBuilder::new().green(&mut pool).spawn(proc() {
//! // this code is running inside the pool of schedulers
//!
//! spawn(proc() {
@ -181,12 +184,9 @@
//! let mut handle = pool.spawn_sched();
//!
//! // Pin a task to the spawned scheduler
//! let task = pool.task(TaskOpts::new(), proc() { /* ... */ });
//! handle.send(PinnedTask(task));
//!
//! // Schedule a task on this new scheduler
//! let task = pool.task(TaskOpts::new(), proc() { /* ... */ });
//! handle.send(TaskFromFriend(task));
//! TaskBuilder::new().green_pinned(&mut pool, &mut handle).spawn(proc() {
//! /* ... */
//! });
//!
//! // Handles keep schedulers alive, so be sure to drop all handles before
//! // destroying the sched pool
@ -209,6 +209,8 @@
// NB this does *not* include globs, please keep it that way.
#![feature(macro_rules, phase)]
#![allow(visible_private_types)]
#![allow(deprecated)]
#![feature(default_type_params)]
#[cfg(test)] #[phase(plugin, link)] extern crate log;
#[cfg(test)] extern crate rustuv;
@ -224,8 +226,9 @@ use std::rt::task::TaskOpts;
use std::rt;
use std::sync::atomics::{SeqCst, AtomicUint, INIT_ATOMIC_UINT};
use std::sync::deque;
use std::task::{TaskBuilder, Spawner};
use sched::{Shutdown, Scheduler, SchedHandle, TaskFromFriend, NewNeighbor};
use sched::{Shutdown, Scheduler, SchedHandle, TaskFromFriend, PinnedTask, NewNeighbor};
use sleeper_list::SleeperList;
use stack::StackPool;
use task::GreenTask;
@ -444,6 +447,7 @@ impl SchedPool {
/// This is useful to create a task which can then be sent to a specific
/// scheduler created by `spawn_sched` (and possibly pin it to that
/// scheduler).
#[deprecated = "use the green and green_pinned methods of GreenTaskBuilder instead"]
pub fn task(&mut self, opts: TaskOpts, f: proc():Send) -> Box<GreenTask> {
GreenTask::configure(&mut self.stack_pool, opts, f)
}
@ -454,6 +458,7 @@ impl SchedPool {
/// New tasks are spawned in a round-robin fashion to the schedulers in this
/// pool, but tasks can certainly migrate among schedulers once they're in
/// the pool.
#[deprecated = "use the green and green_pinned methods of GreenTaskBuilder instead"]
pub fn spawn(&mut self, opts: TaskOpts, f: proc():Send) {
let task = self.task(opts, f);
@ -563,3 +568,54 @@ impl Drop for SchedPool {
}
}
}
/// A spawner for green tasks
pub struct GreenSpawner<'a>{
pool: &'a mut SchedPool,
handle: Option<&'a mut SchedHandle>
}
impl<'a> Spawner for GreenSpawner<'a> {
#[inline]
fn spawn(self, opts: TaskOpts, f: proc():Send) {
let GreenSpawner { pool, handle } = self;
match handle {
None => pool.spawn(opts, f),
Some(h) => h.send(PinnedTask(pool.task(opts, f)))
}
}
}
/// An extension trait adding `green` configuration methods to `TaskBuilder`.
pub trait GreenTaskBuilder {
fn green<'a>(self, &'a mut SchedPool) -> TaskBuilder<GreenSpawner<'a>>;
fn green_pinned<'a>(self, &'a mut SchedPool, &'a mut SchedHandle)
-> TaskBuilder<GreenSpawner<'a>>;
}
impl<S: Spawner> GreenTaskBuilder for TaskBuilder<S> {
fn green<'a>(self, pool: &'a mut SchedPool) -> TaskBuilder<GreenSpawner<'a>> {
self.spawner(GreenSpawner {pool: pool, handle: None})
}
fn green_pinned<'a>(self, pool: &'a mut SchedPool, handle: &'a mut SchedHandle)
-> TaskBuilder<GreenSpawner<'a>> {
self.spawner(GreenSpawner {pool: pool, handle: Some(handle)})
}
}
#[cfg(test)]
mod test {
use std::task::TaskBuilder;
use super::{SchedPool, PoolConfig, GreenTaskBuilder};
#[test]
fn test_green_builder() {
let mut pool = SchedPool::new(PoolConfig::new());
let res = TaskBuilder::new().green(&mut pool).try(proc() {
"Success!".to_string()
});
assert_eq!(res.ok().unwrap(), "Success!".to_string());
pool.shutdown();
}
}

View file

@ -32,10 +32,13 @@
//! ```rust
//! extern crate native;
//!
//! use std::task::TaskBuilder;
//! use native::NativeTaskBuilder;
//!
//! fn main() {
//! // We're not sure whether this main function is run in 1:1 or M:N mode.
//!
//! native::task::spawn(proc() {
//! TaskBuilder::new().native().spawn(proc() {
//! // this code is guaranteed to be run on a native thread
//! });
//! }
@ -50,7 +53,8 @@
html_root_url = "http://doc.rust-lang.org/")]
#![deny(unused_result, unused_must_use)]
#![allow(non_camel_case_types)]
#![feature(macro_rules)]
#![allow(deprecated)]
#![feature(default_type_params)]
// NB this crate explicitly does *not* allow glob imports, please seriously
// consider whether they're needed before adding that feature here (the
@ -65,6 +69,8 @@ use std::os;
use std::rt;
use std::str;
pub use task::NativeTaskBuilder;
pub mod io;
pub mod task;

View file

@ -27,6 +27,7 @@ use std::rt;
use io;
use task;
use std::task::{TaskBuilder, Spawner};
/// Creates a new Task which is ready to execute as a 1:1 task.
pub fn new(stack_bounds: (uint, uint)) -> Box<Task> {
@ -48,12 +49,14 @@ fn ops() -> Box<Ops> {
}
/// Spawns a function with the default configuration
#[deprecated = "use the native method of NativeTaskBuilder instead"]
pub fn spawn(f: proc():Send) {
spawn_opts(TaskOpts { name: None, stack_size: None, on_exit: None }, f)
}
/// Spawns a new task given the configuration options and a procedure to run
/// inside the task.
#[deprecated = "use the native method of NativeTaskBuilder instead"]
pub fn spawn_opts(opts: TaskOpts, f: proc():Send) {
let TaskOpts { name, stack_size, on_exit } = opts;
@ -95,6 +98,26 @@ pub fn spawn_opts(opts: TaskOpts, f: proc():Send) {
})
}
/// A spawner for native tasks
pub struct NativeSpawner;
impl Spawner for NativeSpawner {
fn spawn(self, opts: TaskOpts, f: proc():Send) {
spawn_opts(opts, f)
}
}
/// An extension trait adding a `native` configuration method to `TaskBuilder`.
pub trait NativeTaskBuilder {
fn native(self) -> TaskBuilder<NativeSpawner>;
}
impl<S: Spawner> NativeTaskBuilder for TaskBuilder<S> {
fn native(self) -> TaskBuilder<NativeSpawner> {
self.spawner(NativeSpawner)
}
}
// This structure is the glue between channels and the 1:1 scheduling mode. This
// structure is allocated once per task.
struct Ops {
@ -259,7 +282,8 @@ mod tests {
use std::rt::local::Local;
use std::rt::task::{Task, TaskOpts};
use std::task;
use super::{spawn, spawn_opts, Ops};
use std::task::TaskBuilder;
use super::{spawn, spawn_opts, Ops, NativeTaskBuilder};
#[test]
fn smoke() {
@ -347,4 +371,12 @@ mod tests {
});
rx.recv();
}
#[test]
fn test_native_builder() {
let res = TaskBuilder::new().native().try(proc() {
"Success!".to_string()
});
assert_eq!(res.ok().unwrap(), "Success!".to_string());
}
}

View file

@ -8,71 +8,110 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Utilities for managing and scheduling tasks
*
* An executing Rust program consists of a collection of tasks, each with their
* own stack, and sole ownership of their allocated heap data. Tasks communicate
* with each other using channels (see `std::comm` for more info about how
* communication works).
*
* Failure in one task does not propagate to any others (not to parent, not to
* child). Failure propagation is instead handled by using the channel send()
* and recv() methods which will fail if the other end has hung up already.
*
* Task Scheduling:
*
* By default, every task is created with the same "flavor" as the calling task.
* This flavor refers to the scheduling mode, with two possibilities currently
* being 1:1 and M:N modes. Green (M:N) tasks are cooperatively scheduled and
* native (1:1) tasks are scheduled by the OS kernel.
*
* # Example
*
* ```rust
* spawn(proc() {
* println!("Hello, World!");
* })
* ```
*/
//! Utilities for managing and scheduling tasks
//!
//! An executing Rust program consists of a collection of lightweight tasks,
//! each with their own stack. Tasks communicate with each other using channels
//! (see `std::comm`) or other forms of synchronization (see `std::sync`) that
//! ensure data-race freedom.
//!
//! Failure in one task does immediately propagate to any others (not to parent,
//! not to child). Failure propagation is instead handled as part of task
//! synchronization. For example, the channel `send()` and `recv()` methods will
//! fail if the other end has hung up already.
//!
//! # Basic task scheduling
//!
//! By default, every task is created with the same "flavor" as the calling task.
//! This flavor refers to the scheduling mode, with two possibilities currently
//! being 1:1 and M:N modes. Green (M:N) tasks are cooperatively scheduled and
//! native (1:1) tasks are scheduled by the OS kernel.
//!
//! ## Example
//!
//! ```rust
//! spawn(proc() {
//! println!("Hello, World!");
//! })
//! ```
//!
//! # Advanced task scheduling
//!
//! Task spawning can also be configured to use a particular scheduler, to
//! redirect the new task's output, or to yield a `future` representing the
//! task's final result. The configuration is established using the
//! `TaskBuilder` API:
//!
//! ## Example
//!
//! ```rust
//! extern crate green;
//! extern crate native;
//!
//! use std::task::TaskBuilder;
//! use green::{SchedPool, PoolConfig, GreenTaskBuilder};
//! use native::NativeTaskBuilder;
//!
//! # fn main() {
//! // Create a green scheduler pool with the default configuration
//! let mut pool = SchedPool::new(PoolConfig::new());
//!
//! // Spawn a task in the green pool
//! let mut fut_green = TaskBuilder::new().green(&mut pool).try_future(proc() {
//! /* ... */
//! });
//!
//! // Spawn a native task
//! let mut fut_native = TaskBuilder::new().native().try_future(proc() {
//! /* ... */
//! });
//!
//! // Wait for both tasks to finish, recording their outcome
//! let res_green = fut_green.unwrap();
//! let res_native = fut_native.unwrap();
//!
//! // Shut down the green scheduler pool
//! pool.shutdown();
//! # }
//! ```
use any::Any;
use comm::{Sender, Receiver, channel};
use comm::channel;
use io::{Writer, stdio};
use kinds::{Send, marker};
use option::{None, Some, Option};
use owned::Box;
use result::{Result, Ok, Err};
use result::Result;
use rt::local::Local;
use rt::task;
use rt::task::Task;
use str::{Str, SendStr, IntoMaybeOwned};
use sync::Future;
#[cfg(test)] use any::AnyRefExt;
#[cfg(test)] use owned::AnyOwnExt;
#[cfg(test)] use result;
#[cfg(test)] use str::StrAllocating;
#[cfg(test)] use string::String;
/// Task configuration options
pub struct TaskOpts {
/// Enable lifecycle notifications on the given channel
pub notify_chan: Option<Sender<task::Result>>,
/// A name for the task-to-be, for identification in failure messages
pub name: Option<SendStr>,
/// The size of the stack for the spawned task
pub stack_size: Option<uint>,
/// Task-local stdout
pub stdout: Option<Box<Writer + Send>>,
/// Task-local stderr
pub stderr: Option<Box<Writer + Send>>,
/// A means of spawning a task
pub trait Spawner {
/// Spawn a task, given low-level task options.
fn spawn(self, opts: task::TaskOpts, f: proc():Send);
}
/**
* The task builder type.
*
* Provides detailed control over the properties and behavior of new tasks.
*/
/// The default task spawner, which spawns siblings to the current task.
pub struct SiblingSpawner;
impl Spawner for SiblingSpawner {
fn spawn(self, opts: task::TaskOpts, f: proc():Send) {
// bind tb to provide type annotation
let tb: Option<Box<Task>> = Local::try_take();
match tb {
Some(t) => t.spawn_sibling(opts, f),
None => fail!("need a local task to spawn a sibling task"),
};
}
}
/// The task builder type.
///
/// Provides detailed control over the properties and behavior of new tasks.
// NB: Builders are designed to be single-use because they do stateful
// things that get weird when reusing - e.g. if you create a result future
// it only applies to a single task, so then you have to maintain Some
@ -80,75 +119,102 @@ pub struct TaskOpts {
// when you try to reuse the builder to spawn a new task. We'll just
// sidestep that whole issue by making builders uncopyable and making
// the run function move them in.
pub struct TaskBuilder {
/// Options to spawn the new task with
pub opts: TaskOpts,
pub struct TaskBuilder<S = SiblingSpawner> {
// A name for the task-to-be, for identification in failure messages
name: Option<SendStr>,
// The size of the stack for the spawned task
stack_size: Option<uint>,
// Task-local stdout
stdout: Option<Box<Writer + Send>>,
// Task-local stderr
stderr: Option<Box<Writer + Send>>,
// The mechanics of actually spawning the task (i.e.: green or native)
spawner: S,
// Optionally wrap the eventual task body
gen_body: Option<proc(v: proc():Send):Send -> proc():Send>,
nocopy: marker::NoCopy,
}
impl TaskBuilder {
impl TaskBuilder<SiblingSpawner> {
/// Generate the base configuration for spawning a task, off of which more
/// configuration methods can be chained.
pub fn new() -> TaskBuilder {
pub fn new() -> TaskBuilder<SiblingSpawner> {
TaskBuilder {
opts: TaskOpts::new(),
name: None,
stack_size: None,
stdout: None,
stderr: None,
spawner: SiblingSpawner,
gen_body: None,
nocopy: marker::NoCopy,
}
}
/// Get a future representing the exit status of the task.
///
/// Taking the value of the future will block until the child task
/// terminates. The future result return value will be created *before* the task is
/// spawned; as such, do not invoke .get() on it directly;
/// rather, store it in an outer variable/list for later use.
///
/// # Failure
/// Fails if a future_result was already set for this task.
pub fn future_result(&mut self) -> Receiver<task::Result> {
// FIXME (#3725): Once linked failure and notification are
// handled in the library, I can imagine implementing this by just
// registering an arbitrary number of task::on_exit handlers and
// sending out messages.
if self.opts.notify_chan.is_some() {
fail!("Can't set multiple future_results for one task!");
}
// Construct the future and give it to the caller.
let (tx, rx) = channel();
// Reconfigure self to use a notify channel.
self.opts.notify_chan = Some(tx);
rx
}
impl<S: Spawner> TaskBuilder<S> {
/// Name the task-to-be. Currently the name is used for identification
/// only in failure messages.
pub fn named<S: IntoMaybeOwned<'static>>(mut self, name: S) -> TaskBuilder {
self.opts.name = Some(name.into_maybe_owned());
pub fn named<T: IntoMaybeOwned<'static>>(mut self, name: T) -> TaskBuilder<S> {
self.name = Some(name.into_maybe_owned());
self
}
/**
* Add a wrapper to the body of the spawned task.
*
* Before the task is spawned it is passed through a 'body generator'
* function that may perform local setup operations as well as wrap
* the task body in remote setup operations. With this the behavior
* of tasks can be extended in simple ways.
*
* This function augments the current body generator with a new body
* generator by applying the task body which results from the
* existing body generator to the new body generator.
*/
pub fn with_wrapper(mut self,
wrapper: proc(v: proc(): Send): Send -> proc(): Send)
-> TaskBuilder
{
/// Set the size of the stack for the new task.
pub fn stack_size(mut self, size: uint) -> TaskBuilder<S> {
self.stack_size = Some(size);
self
}
/// Redirect task-local stdout.
pub fn stdout(mut self, stdout: Box<Writer + Send>) -> TaskBuilder<S> {
self.stdout = Some(stdout);
self
}
/// Redirect task-local stderr.
pub fn stderr(mut self, stderr: Box<Writer + Send>) -> TaskBuilder<S> {
self.stderr = Some(stderr);
self
}
/// Set the spawning mechanism for the task.
///
/// The `TaskBuilder` API configures a task to be spawned, but defers to the
/// "spawner" to actually create and spawn the task. The `spawner` method
/// should not be called directly by `TaskBuiler` clients. It is intended
/// for use by downstream crates (like `native` and `green`) that implement
/// tasks. These downstream crates then add extension methods to the
/// builder, like `.native()` and `.green(pool)`, that actually set the
/// spawner.
pub fn spawner<T: Spawner>(self, spawner: T) -> TaskBuilder<T> {
// repackage the entire TaskBuilder since its type is changing.
let TaskBuilder {
name, stack_size, stdout, stderr, spawner: _, gen_body, nocopy
} = self;
TaskBuilder {
name: name,
stack_size: stack_size,
stdout: stdout,
stderr: stderr,
spawner: spawner,
gen_body: gen_body,
nocopy: nocopy,
}
}
/// Add a wrapper to the body of the spawned task.
///
/// Before the task is spawned it is passed through a 'body generator'
/// function that may perform local setup operations as well as wrap
/// the task body in remote setup operations. With this the behavior
/// of tasks can be extended in simple ways.
///
/// This function augments the current body generator with a new body
/// generator by applying the task body which results from the
/// existing body generator to the new body generator.
#[deprecated = "this function will be removed soon"]
pub fn with_wrapper(mut self, wrapper: proc(v: proc():Send):Send -> proc():Send)
-> TaskBuilder<S> {
self.gen_body = match self.gen_body.take() {
Some(prev) => Some(proc(body) { wrapper(prev(body)) }),
None => Some(wrapper)
@ -156,90 +222,80 @@ impl TaskBuilder {
self
}
/**
* Creates and executes a new child task
*
* Sets up a new task with its own call stack and schedules it to run
* the provided unique closure. The task has the properties and behavior
* specified by the task_builder.
*/
pub fn spawn(mut self, f: proc(): Send) {
let gen_body = self.gen_body.take();
let f = match gen_body {
// Where spawning actually happens (whether yielding a future or not)
fn spawn_internal(self, f: proc():Send,
on_exit: Option<proc(Result<(), Box<Any + Send>>):Send>) {
let TaskBuilder {
name, stack_size, stdout, stderr, spawner, mut gen_body, nocopy: _
} = self;
let f = match gen_body.take() {
Some(gen) => gen(f),
None => f
};
let t: Box<Task> = match Local::try_take() {
Some(t) => t,
None => fail!("need a local task to spawn a new task"),
};
let TaskOpts { notify_chan, name, stack_size, stdout, stderr } = self.opts;
let opts = task::TaskOpts {
on_exit: notify_chan.map(|c| proc(r) c.send(r)),
on_exit: on_exit,
name: name,
stack_size: stack_size,
};
if stdout.is_some() || stderr.is_some() {
t.spawn_sibling(opts, proc() {
spawner.spawn(opts, proc() {
let _ = stdout.map(stdio::set_stdout);
let _ = stderr.map(stdio::set_stderr);
f();
});
})
} else {
t.spawn_sibling(opts, f);
spawner.spawn(opts, f)
}
}
/**
* Execute a function in another task and return either the return value
* of the function or result::err.
*
* # Return value
*
* If the function executed successfully then try returns result::ok
* containing the value returned by the function. If the function fails
* then try returns result::err containing nil.
*
* # Failure
* Fails if a future_result was already set for this task.
*/
pub fn try<T: Send>(mut self, f: proc(): Send -> T)
-> Result<T, Box<Any + Send>> {
let (tx, rx) = channel();
let result = self.future_result();
self.spawn(proc() {
tx.send(f());
});
match result.recv() {
Ok(()) => Ok(rx.recv()),
Err(cause) => Err(cause)
}
}
/// Creates and executes a new child task.
///
/// Sets up a new task with its own call stack and schedules it to run
/// the provided proc. The task has the properties and behavior
/// specified by the `TaskBuilder`.
pub fn spawn(self, f: proc():Send) {
self.spawn_internal(f, None)
}
/* Task construction */
/// Execute a proc in a newly-spawned task and return a future representing
/// the task's result. The task has the properties and behavior
/// specified by the `TaskBuilder`.
///
/// Taking the value of the future will block until the child task
/// terminates.
///
/// # Return value
///
/// If the child task executes successfully (without failing) then the
/// future returns `result::Ok` containing the value returned by the
/// function. If the child task fails then the future returns `result::Err`
/// containing the argument to `fail!(...)` as an `Any` trait object.
pub fn try_future<T:Send>(self, f: proc():Send -> T)
-> Future<Result<T, Box<Any + Send>>> {
// currently, the on_exit proc provided by librustrt only works for unit
// results, so we use an additional side-channel to communicate the
// result.
impl TaskOpts {
pub fn new() -> TaskOpts {
/*!
* The default task options
*/
let (tx_done, rx_done) = channel(); // signal that task has exited
let (tx_retv, rx_retv) = channel(); // return value from task
TaskOpts {
notify_chan: None,
name: None,
stack_size: None,
stdout: None,
stderr: None,
let on_exit = proc(res) { tx_done.send(res) };
self.spawn_internal(proc() { tx_retv.send(f()) },
Some(on_exit));
Future::from_fn(proc() {
rx_done.recv().map(|_| rx_retv.recv())
})
}
/// Execute a function in a newly-spawnedtask and block until the task
/// completes or fails. Equivalent to `.try_future(f).unwrap()`.
pub fn try<T:Send>(self, f: proc():Send -> T) -> Result<T, Box<Any + Send>> {
self.try_future(f).unwrap()
}
}
/* Spawn convenience functions */
/* Convenience functions */
/// Creates and executes a new child task
///
@ -251,14 +307,22 @@ pub fn spawn(f: proc(): Send) {
TaskBuilder::new().spawn(f)
}
/// Execute a function in another task and return either the return value of
/// the function or an error if the task failed
/// Execute a function in a newly-spawned task and return either the return
/// value of the function or an error if the task failed.
///
/// This is equivalent to TaskBuilder::new().try
/// This is equivalent to `TaskBuilder::new().try`.
pub fn try<T: Send>(f: proc(): Send -> T) -> Result<T, Box<Any + Send>> {
TaskBuilder::new().try(f)
}
/// Execute a function in another task and return a future representing the
/// task's result.
///
/// This is equivalent to `TaskBuilder::new().try_future`.
pub fn try_future<T:Send>(f: proc():Send -> T) -> Future<Result<T, Box<Any + Send>>> {
TaskBuilder::new().try_future(f)
}
/* Lifecycle functions */
@ -273,9 +337,8 @@ pub fn with_task_name<U>(blk: |Option<&str>| -> U) -> U {
}
}
/// Yield control to the task scheduler.
pub fn deschedule() {
//! Yield control to the task scheduler
use rt::local::Local;
// FIXME(#7544): Optimize this, since we know we won't block.
@ -283,16 +346,26 @@ pub fn deschedule() {
task.yield_now();
}
/// True if the running task is currently failing (e.g. will return `true` inside a
/// destructor that is run while unwinding the stack after a call to `fail!()`).
pub fn failing() -> bool {
//! True if the running task has failed
use rt::task::Task;
Local::borrow(None::<Task>).unwinder.unwinding()
}
// The following 8 tests test the following 2^3 combinations:
// {un,}linked {un,}supervised failure propagation {up,down}wards.
#[cfg(test)]
mod test {
use any::{Any, AnyRefExt};
use owned::AnyOwnExt;
use result;
use result::{Ok, Err};
use str::StrAllocating;
use string::String;
use std::io::{ChanReader, ChanWriter};
use prelude::*;
use super::*;
// !!! These tests are dangerous. If Something is buggy, they will hang, !!!
// !!! These tests are dangerous. If something is buggy, they will hang, !!!
// !!! instead of exiting cleanly. This might wedge the buildbots. !!!
#[test]
@ -354,25 +427,14 @@ fn test_with_wrapper() {
}
#[test]
fn test_future_result() {
let mut builder = TaskBuilder::new();
let result = builder.future_result();
builder.spawn(proc() {});
assert!(result.recv().is_ok());
fn test_try_future() {
let result = TaskBuilder::new().try_future(proc() {});
assert!(result.unwrap().is_ok());
let mut builder = TaskBuilder::new();
let result = builder.future_result();
builder.spawn(proc() {
let result = TaskBuilder::new().try_future(proc() -> () {
fail!();
});
assert!(result.recv().is_err());
}
#[test] #[should_fail]
fn test_back_to_the_future_result() {
let mut builder = TaskBuilder::new();
builder.future_result();
builder.future_result();
assert!(result.unwrap().is_err());
}
#[test]
@ -429,7 +491,6 @@ fn test_spawn_sched_childs_on_default_sched() {
rx.recv();
}
#[cfg(test)]
fn avoid_copying_the_body(spawnfn: |v: proc():Send|) {
let (tx, rx) = channel::<uint>();
@ -546,3 +607,21 @@ fn test_try_fail_message_unit_struct() {
Err(_) | Ok(()) => fail!()
}
}
#[test]
fn test_stdout() {
let (tx, rx) = channel();
let mut reader = ChanReader::new(rx);
let stdout = ChanWriter::new(tx);
TaskBuilder::new().stdout(box stdout as Box<Writer + Send>).try(proc() {
print!("Hello, world!");
}).unwrap();
let output = reader.read_to_str().unwrap();
assert_eq!(output, "Hello, world!".to_string());
}
// NOTE: the corresponding test for stderr is in run-pass/task-stderr, due
// to the test harness apparently interfering with stderr configuration.
}

View file

@ -0,0 +1,26 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::io::{ChanReader, ChanWriter};
use std::task::build;
fn main() {
let (tx, rx) = channel();
let mut reader = ChanReader::new(rx);
let stderr = ChanWriter::new(tx);
let res = build().stderr(box stderr as Box<Writer + Send>).try(proc() -> () {
fail!("Hello, world!")
});
assert!(res.is_err());
let output = reader.read_to_str().unwrap();
assert!(output.as_slice().contains("Hello, world!"));
}