2013-05-30 05:16:33 -05:00
|
|
|
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
2012-12-03 18:48:01 -06:00
|
|
|
// 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.
|
|
|
|
|
2012-11-28 18:20:41 -06:00
|
|
|
/*!
|
|
|
|
* Task management.
|
|
|
|
*
|
|
|
|
* An executing Rust program consists of a tree of tasks, each with their own
|
|
|
|
* stack, and sole ownership of their allocated heap data. Tasks communicate
|
2013-09-27 16:22:16 -05:00
|
|
|
* with each other using ports and channels (see std::rt::comm for more info
|
|
|
|
* about how communication works).
|
2012-11-28 18:20:41 -06:00
|
|
|
*
|
2013-09-27 16:22:16 -05:00
|
|
|
* Tasks can be spawned in 3 different modes.
|
2012-11-28 18:20:41 -06:00
|
|
|
*
|
2013-09-27 16:22:16 -05:00
|
|
|
* * Bidirectionally linked: This is the default mode and it's what ```spawn``` does.
|
|
|
|
* Failures will be propagated from parent to child and vice versa.
|
|
|
|
*
|
|
|
|
* * Unidirectionally linked (parent->child): This type of task can be created with
|
|
|
|
* ```spawn_supervised```. In this case, failures are propagated from parent to child
|
|
|
|
* but not the other way around.
|
|
|
|
*
|
|
|
|
* * Unlinked: Tasks can be completely unlinked. These tasks can be created by using
|
|
|
|
* ```spawn_unlinked```. In this case failures are not propagated at all.
|
|
|
|
*
|
|
|
|
* Tasks' failure modes can be further configured. For instance, parent tasks can (un)watch
|
|
|
|
* children failures. Please, refer to TaskBuilder's documentation bellow for more information.
|
|
|
|
*
|
|
|
|
* When a (bi|uni)directionally linked task fails, its failure will be propagated to all tasks
|
|
|
|
* linked to it, this will cause such tasks to fail by a `linked failure`.
|
|
|
|
*
|
|
|
|
* Task Scheduling:
|
|
|
|
*
|
|
|
|
* By default, every task is created in the same scheduler as its parent, where it
|
|
|
|
* is scheduled cooperatively with all other tasks in that scheduler. Some specialized
|
|
|
|
* applications may want more control over their scheduling, in which case they can be
|
|
|
|
* spawned into a new scheduler with the specific properties required. See TaskBuilder's
|
|
|
|
* documentation bellow for more information.
|
2012-11-28 18:20:41 -06:00
|
|
|
*
|
|
|
|
* # Example
|
|
|
|
*
|
2013-09-23 19:20:36 -05:00
|
|
|
* ```
|
2012-11-28 18:20:41 -06:00
|
|
|
* do spawn {
|
|
|
|
* log(error, "Hello, World!");
|
|
|
|
* }
|
2013-09-23 19:20:36 -05:00
|
|
|
* ```
|
2012-11-28 18:20:41 -06:00
|
|
|
*/
|
2012-11-18 19:56:50 -06:00
|
|
|
|
2013-05-28 16:35:52 -05:00
|
|
|
#[allow(missing_doc)];
|
|
|
|
|
2013-05-24 21:35:29 -05:00
|
|
|
use prelude::*;
|
|
|
|
|
2013-10-11 16:20:34 -05:00
|
|
|
use comm::{stream, Chan, GenericChan, GenericPort, Port, Peekable};
|
|
|
|
use result::{Result, Ok, Err};
|
2013-08-01 01:12:20 -05:00
|
|
|
use rt::in_green_task_context;
|
2013-07-08 12:48:57 -05:00
|
|
|
use rt::local::Local;
|
2013-10-11 16:20:34 -05:00
|
|
|
use rt::task::{UnwindResult, Success, Failure};
|
|
|
|
use send_str::{SendStr, IntoSendStr};
|
2013-05-24 21:35:29 -05:00
|
|
|
use util;
|
2012-11-18 19:56:50 -06:00
|
|
|
|
2013-10-11 16:20:34 -05:00
|
|
|
#[cfg(test)] use any::Any;
|
2013-03-26 15:38:07 -05:00
|
|
|
#[cfg(test)] use comm::SharedChan;
|
2013-05-30 05:16:33 -05:00
|
|
|
#[cfg(test)] use ptr;
|
2013-10-11 16:20:34 -05:00
|
|
|
#[cfg(test)] use result;
|
2013-03-26 15:38:07 -05:00
|
|
|
|
2012-11-18 19:56:50 -06:00
|
|
|
pub mod spawn;
|
2012-11-28 18:20:41 -06:00
|
|
|
|
2013-10-11 16:20:34 -05:00
|
|
|
/// Indicates the manner in which a task exited.
|
|
|
|
///
|
|
|
|
/// A task that completes without failing is considered to exit successfully.
|
|
|
|
/// Supervised ancestors and linked siblings may yet fail after this task
|
|
|
|
/// succeeds. Also note that in such a case, it may be nondeterministic whether
|
|
|
|
/// linked failure or successful exit happen first.
|
|
|
|
///
|
|
|
|
/// If you wish for this result's delivery to block until all linked and/or
|
|
|
|
/// children tasks complete, recommend using a result future.
|
|
|
|
pub type TaskResult = Result<(), ~Any>;
|
|
|
|
|
|
|
|
pub struct TaskResultPort {
|
|
|
|
priv port: Port<UnwindResult>
|
|
|
|
}
|
|
|
|
|
2013-11-01 13:20:01 -05:00
|
|
|
fn to_task_result(res: UnwindResult) -> TaskResult {
|
|
|
|
match res {
|
|
|
|
Success => Ok(()), Failure(a) => Err(a),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-11 16:20:34 -05:00
|
|
|
impl GenericPort<TaskResult> for TaskResultPort {
|
|
|
|
#[inline]
|
|
|
|
fn recv(&self) -> TaskResult {
|
2013-11-01 13:20:01 -05:00
|
|
|
to_task_result(self.port.recv())
|
2013-10-11 16:20:34 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn try_recv(&self) -> Option<TaskResult> {
|
2013-11-01 13:20:01 -05:00
|
|
|
self.port.try_recv().map(to_task_result)
|
2013-10-11 16:20:34 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Peekable<TaskResult> for TaskResultPort {
|
|
|
|
#[inline]
|
|
|
|
fn peek(&self) -> bool { self.port.peek() }
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Scheduler modes
|
2013-03-20 10:35:02 -05:00
|
|
|
#[deriving(Eq)]
|
2012-11-28 18:20:41 -06:00
|
|
|
pub enum SchedMode {
|
2013-01-08 21:46:12 -06:00
|
|
|
/// Run task on the default scheduler
|
|
|
|
DefaultScheduler,
|
2012-11-28 18:20:41 -06:00
|
|
|
/// All tasks run in the same OS thread
|
|
|
|
SingleThreaded,
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scheduler configuration options
|
|
|
|
*
|
|
|
|
* # Fields
|
|
|
|
*
|
|
|
|
* * sched_mode - The operating mode of the scheduler
|
|
|
|
*
|
|
|
|
*/
|
2013-01-22 10:12:52 -06:00
|
|
|
pub struct SchedOpts {
|
2013-10-19 19:33:09 -05:00
|
|
|
priv mode: SchedMode,
|
2013-01-22 10:12:52 -06:00
|
|
|
}
|
2012-11-28 18:20:41 -06:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Task configuration options
|
|
|
|
*
|
|
|
|
* # Fields
|
|
|
|
*
|
2013-07-16 16:28:46 -05:00
|
|
|
* * watched - Make parent task collect exit status notifications from child
|
|
|
|
* before reporting its own exit status. (This delays the parent
|
|
|
|
* task's death and cleanup until after all transitively watched
|
|
|
|
* children also exit.) True by default.
|
|
|
|
*
|
2012-11-28 18:20:41 -06:00
|
|
|
* * notify_chan - Enable lifecycle notifications on the given channel
|
|
|
|
*
|
2013-07-30 18:20:59 -05:00
|
|
|
* * name - A name for the task-to-be, for identification in failure messages.
|
|
|
|
*
|
2012-11-28 18:20:41 -06:00
|
|
|
* * sched - Specify the configuration of a new scheduler to create the task
|
2013-09-27 16:22:16 -05:00
|
|
|
* in. This is of particular importance for libraries which want to call
|
|
|
|
* into foreign code that blocks. Without doing so in a different
|
|
|
|
* scheduler other tasks will be impeded or even blocked indefinitely.
|
2012-11-28 18:20:41 -06:00
|
|
|
*/
|
2013-01-22 10:12:52 -06:00
|
|
|
pub struct TaskOpts {
|
2013-10-19 22:26:42 -05:00
|
|
|
priv watched: bool,
|
2013-10-11 16:20:34 -05:00
|
|
|
priv notify_chan: Option<Chan<UnwindResult>>,
|
2013-10-19 22:26:42 -05:00
|
|
|
name: Option<SendStr>,
|
|
|
|
sched: SchedOpts,
|
|
|
|
stack_size: Option<uint>
|
2013-01-22 10:12:52 -06:00
|
|
|
}
|
2012-11-28 18:20:41 -06:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
// potentially tricky state to ensure that everything behaves correctly
|
|
|
|
// 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.
|
2012-12-05 17:06:54 -06:00
|
|
|
pub struct TaskBuilder {
|
2012-11-28 18:20:41 -06:00
|
|
|
opts: TaskOpts,
|
2013-11-18 15:25:09 -06:00
|
|
|
priv gen_body: Option<proc(v: proc()) -> proc()>,
|
2013-10-19 19:33:09 -05:00
|
|
|
priv can_not_copy: Option<util::NonCopyable>,
|
2012-12-05 17:06:54 -06:00
|
|
|
}
|
2012-11-28 18:20:41 -06:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate the base configuration for spawning a task, off of which more
|
|
|
|
* configuration methods can be chained.
|
|
|
|
* For example, task().unlinked().spawn is equivalent to spawn_unlinked.
|
|
|
|
*/
|
|
|
|
pub fn task() -> TaskBuilder {
|
2012-12-05 17:06:54 -06:00
|
|
|
TaskBuilder {
|
2012-11-28 18:20:41 -06:00
|
|
|
opts: default_task_opts(),
|
2013-04-18 20:22:04 -05:00
|
|
|
gen_body: None,
|
2012-11-28 18:20:41 -06:00
|
|
|
can_not_copy: None,
|
2012-12-05 17:06:54 -06:00
|
|
|
}
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
2013-05-31 17:17:22 -05:00
|
|
|
impl TaskBuilder {
|
2013-11-21 18:55:40 -06:00
|
|
|
fn consume(mut self) -> TaskBuilder {
|
2013-07-17 16:41:50 -05:00
|
|
|
let gen_body = self.gen_body.take();
|
|
|
|
let notify_chan = self.opts.notify_chan.take();
|
2013-07-30 18:20:59 -05:00
|
|
|
let name = self.opts.name.take();
|
2012-12-05 17:06:54 -06:00
|
|
|
TaskBuilder {
|
2013-01-22 10:12:52 -06:00
|
|
|
opts: TaskOpts {
|
2013-07-16 16:28:46 -05:00
|
|
|
watched: self.opts.watched,
|
2013-01-22 10:12:52 -06:00
|
|
|
notify_chan: notify_chan,
|
2013-07-30 18:20:59 -05:00
|
|
|
name: name,
|
2013-08-05 15:10:08 -05:00
|
|
|
sched: self.opts.sched,
|
|
|
|
stack_size: self.opts.stack_size
|
2012-11-28 18:20:41 -06:00
|
|
|
},
|
2013-04-18 20:22:04 -05:00
|
|
|
gen_body: gen_body,
|
2012-11-28 18:20:41 -06:00
|
|
|
can_not_copy: None,
|
2012-12-05 17:06:54 -06:00
|
|
|
}
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
2013-07-16 16:28:46 -05:00
|
|
|
/// Cause the parent task to collect the child's exit status (and that of
|
|
|
|
/// all transitively-watched grandchildren) before reporting its own.
|
|
|
|
pub fn watched(&mut self) {
|
|
|
|
self.opts.watched = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Allow the child task to outlive the parent task, at the possible cost
|
|
|
|
/// of the parent reporting success even if the child task fails later.
|
|
|
|
pub fn unwatched(&mut self) {
|
|
|
|
self.opts.watched = false;
|
|
|
|
}
|
|
|
|
|
2013-10-18 03:38:46 -05:00
|
|
|
/// 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.
|
|
|
|
///
|
|
|
|
/// Note that the future returned by this function is only useful for
|
|
|
|
/// obtaining the value of the next task to be spawning with the
|
|
|
|
/// builder. If additional tasks are spawned with the same builder
|
|
|
|
/// then a new result future must be obtained prior to spawning each
|
|
|
|
/// task.
|
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
/// Fails if a future_result was already set for this task.
|
2013-10-11 16:20:34 -05:00
|
|
|
pub fn future_result(&mut self) -> TaskResultPort {
|
2012-11-28 18:20:41 -06:00
|
|
|
// 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() {
|
2013-10-21 15:08:31 -05:00
|
|
|
fail!("Can't set multiple future_results for one task!");
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Construct the future and give it to the caller.
|
2013-10-11 16:20:34 -05:00
|
|
|
let (notify_pipe_po, notify_pipe_ch) = stream::<UnwindResult>();
|
2012-11-28 18:20:41 -06:00
|
|
|
|
|
|
|
// Reconfigure self to use a notify channel.
|
2013-05-03 15:21:33 -05:00
|
|
|
self.opts.notify_chan = Some(notify_pipe_ch);
|
2013-10-18 03:38:46 -05:00
|
|
|
|
2013-10-11 16:20:34 -05:00
|
|
|
TaskResultPort { port: notify_pipe_po }
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
2013-05-03 15:21:33 -05:00
|
|
|
|
2013-07-30 18:20:59 -05:00
|
|
|
/// Name the task-to-be. Currently the name is used for identification
|
|
|
|
/// only in failure messages.
|
2013-10-05 14:01:58 -05:00
|
|
|
pub fn name<S: IntoSendStr>(&mut self, name: S) {
|
|
|
|
self.opts.name = Some(name.into_send_str());
|
2013-07-30 18:20:59 -05:00
|
|
|
}
|
|
|
|
|
2012-11-28 18:20:41 -06:00
|
|
|
/// Configure a custom scheduler mode for the task.
|
2013-05-31 17:17:22 -05:00
|
|
|
pub fn sched_mode(&mut self, mode: SchedMode) {
|
2013-05-03 15:21:33 -05:00
|
|
|
self.opts.sched.mode = mode;
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2013-11-18 15:25:09 -06:00
|
|
|
pub fn add_wrapper(&mut self, wrapper: proc(v: proc()) -> proc()) {
|
2013-07-17 16:41:50 -05:00
|
|
|
let prev_gen_body = self.gen_body.take();
|
2013-04-18 20:22:04 -05:00
|
|
|
let prev_gen_body = match prev_gen_body {
|
|
|
|
Some(gen) => gen,
|
|
|
|
None => {
|
2013-11-22 01:36:52 -06:00
|
|
|
let f: proc(proc()) -> proc() = proc(body) body;
|
2013-04-18 20:22:04 -05:00
|
|
|
f
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let next_gen_body = {
|
2013-11-22 01:36:52 -06:00
|
|
|
let f: proc(proc()) -> proc() = proc(body) {
|
2013-04-18 20:22:04 -05:00
|
|
|
wrapper(prev_gen_body(body))
|
|
|
|
};
|
|
|
|
f
|
|
|
|
};
|
2013-05-03 15:21:33 -05:00
|
|
|
self.gen_body = Some(next_gen_body);
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* # Failure
|
|
|
|
*
|
|
|
|
* When spawning into a new scheduler, the number of threads requested
|
|
|
|
* must be greater than zero.
|
|
|
|
*/
|
2013-11-21 18:55:40 -06:00
|
|
|
pub fn spawn(mut self, f: proc()) {
|
2013-07-17 16:41:50 -05:00
|
|
|
let gen_body = self.gen_body.take();
|
|
|
|
let notify_chan = self.opts.notify_chan.take();
|
2013-07-30 18:20:59 -05:00
|
|
|
let name = self.opts.name.take();
|
2012-11-28 18:20:41 -06:00
|
|
|
let x = self.consume();
|
2013-01-22 10:12:52 -06:00
|
|
|
let opts = TaskOpts {
|
2013-07-16 16:28:46 -05:00
|
|
|
watched: x.opts.watched,
|
2013-01-22 10:12:52 -06:00
|
|
|
notify_chan: notify_chan,
|
2013-07-30 18:20:59 -05:00
|
|
|
name: name,
|
2013-08-05 15:10:08 -05:00
|
|
|
sched: x.opts.sched,
|
|
|
|
stack_size: x.opts.stack_size
|
2012-11-28 18:20:41 -06:00
|
|
|
};
|
2013-04-18 20:22:04 -05:00
|
|
|
let f = match gen_body {
|
|
|
|
Some(gen) => {
|
|
|
|
gen(f)
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
f
|
|
|
|
}
|
|
|
|
};
|
|
|
|
spawn::spawn_raw(opts, f);
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
2013-05-03 15:21:33 -05:00
|
|
|
|
2012-11-28 18:20:41 -06:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2013-11-21 18:55:40 -06:00
|
|
|
pub fn try<T:Send>(mut self, f: proc() -> T) -> Result<T, ~Any> {
|
2013-01-22 14:38:08 -06:00
|
|
|
let (po, ch) = stream::<T>();
|
2012-11-28 18:20:41 -06:00
|
|
|
|
2013-10-18 03:38:46 -05:00
|
|
|
let result = self.future_result();
|
2013-05-03 15:21:33 -05:00
|
|
|
|
|
|
|
do self.spawn {
|
2013-01-22 14:38:08 -06:00
|
|
|
ch.send(f());
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
2013-05-03 15:21:33 -05:00
|
|
|
|
2013-10-18 03:38:46 -05:00
|
|
|
match result.recv() {
|
2013-10-11 16:20:34 -05:00
|
|
|
Ok(()) => Ok(po.recv()),
|
|
|
|
Err(cause) => Err(cause)
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Task construction */
|
|
|
|
|
|
|
|
pub fn default_task_opts() -> TaskOpts {
|
|
|
|
/*!
|
|
|
|
* The default task options
|
|
|
|
*
|
|
|
|
* By default all tasks are supervised by their parent, are spawned
|
|
|
|
* into the same scheduler, and do not post lifecycle notifications.
|
|
|
|
*/
|
|
|
|
|
2013-01-22 10:12:52 -06:00
|
|
|
TaskOpts {
|
2013-07-16 16:28:46 -05:00
|
|
|
watched: true,
|
2013-01-22 10:12:52 -06:00
|
|
|
notify_chan: None,
|
2013-07-30 18:20:59 -05:00
|
|
|
name: None,
|
2013-01-25 19:51:53 -06:00
|
|
|
sched: SchedOpts {
|
2013-01-08 21:46:12 -06:00
|
|
|
mode: DefaultScheduler,
|
2013-08-05 15:10:08 -05:00
|
|
|
},
|
|
|
|
stack_size: None
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Spawn convenience functions */
|
|
|
|
|
2013-05-03 15:21:33 -05:00
|
|
|
/// 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.
|
|
|
|
///
|
|
|
|
/// This function is equivalent to `task().spawn(f)`.
|
2013-11-18 15:25:09 -06:00
|
|
|
pub fn spawn(f: proc()) {
|
2013-11-21 18:55:40 -06:00
|
|
|
let task = task();
|
2013-05-03 15:21:33 -05:00
|
|
|
task.spawn(f)
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
2013-11-18 15:25:09 -06:00
|
|
|
pub fn spawn_sched(mode: SchedMode, f: proc()) {
|
2012-11-28 18:20:41 -06:00
|
|
|
/*!
|
2013-10-26 19:05:55 -05:00
|
|
|
* Creates a new task on a new or existing scheduler.
|
|
|
|
*
|
2013-01-08 21:46:12 -06:00
|
|
|
* When there are no more tasks to execute the
|
2012-11-28 18:20:41 -06:00
|
|
|
* scheduler terminates.
|
|
|
|
*
|
|
|
|
* # Failure
|
|
|
|
*
|
|
|
|
* In manual threads mode the number of threads requested must be
|
|
|
|
* greater than zero.
|
|
|
|
*/
|
|
|
|
|
2013-05-03 15:21:33 -05:00
|
|
|
let mut task = task();
|
|
|
|
task.sched_mode(mode);
|
|
|
|
task.spawn(f)
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
2013-11-18 15:25:09 -06:00
|
|
|
pub fn try<T:Send>(f: proc() -> T) -> Result<T, ~Any> {
|
2012-11-28 18:20:41 -06:00
|
|
|
/*!
|
|
|
|
* Execute a function in another task and return either the return value
|
|
|
|
* of the function or result::err.
|
|
|
|
*
|
|
|
|
* This is equivalent to task().supervised().try.
|
|
|
|
*/
|
|
|
|
|
2013-11-21 18:55:40 -06:00
|
|
|
let task = task();
|
2013-05-03 15:21:33 -05:00
|
|
|
task.try(f)
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Lifecycle functions */
|
|
|
|
|
2013-07-30 18:20:59 -05:00
|
|
|
/// Read the name of the current task.
|
2013-11-18 23:15:42 -06:00
|
|
|
pub fn with_task_name<U>(blk: |Option<&str>| -> U) -> U {
|
2013-07-30 18:20:59 -05:00
|
|
|
use rt::task::Task;
|
|
|
|
|
2013-08-09 03:15:31 -05:00
|
|
|
if in_green_task_context() {
|
2013-12-03 21:18:58 -06:00
|
|
|
let mut task = Local::borrow(None::<Task>);
|
|
|
|
match task.get().name {
|
|
|
|
Some(ref name) => blk(Some(name.as_slice())),
|
|
|
|
None => blk(None)
|
|
|
|
}
|
2013-08-09 03:15:31 -05:00
|
|
|
} else {
|
2013-10-21 15:08:31 -05:00
|
|
|
fail!("no task name exists in non-green task context")
|
2013-07-30 18:20:59 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-16 14:49:40 -05:00
|
|
|
pub fn deschedule() {
|
2012-11-28 18:20:41 -06:00
|
|
|
//! Yield control to the task scheduler
|
|
|
|
|
2013-07-08 20:06:17 -05:00
|
|
|
use rt::local::Local;
|
2013-10-22 17:09:23 -05:00
|
|
|
use rt::sched::Scheduler;
|
2013-07-08 20:06:17 -05:00
|
|
|
|
2013-08-01 01:12:20 -05:00
|
|
|
// FIXME(#7544): Optimize this, since we know we won't block.
|
2013-08-08 13:38:10 -05:00
|
|
|
let sched: ~Scheduler = Local::take();
|
2013-09-20 20:49:31 -05:00
|
|
|
sched.yield_now();
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn failing() -> bool {
|
|
|
|
//! True if the running task has failed
|
|
|
|
|
2013-05-19 18:50:21 -05:00
|
|
|
use rt::task::Task;
|
2013-04-22 19:15:31 -05:00
|
|
|
|
2013-12-03 21:18:58 -06:00
|
|
|
let mut local = Local::borrow(None::<Task>);
|
|
|
|
local.get().unwinder.unwinding
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// The following 8 tests test the following 2^3 combinations:
|
|
|
|
// {un,}linked {un,}supervised failure propagation {up,down}wards.
|
|
|
|
|
|
|
|
// !!! These tests are dangerous. If Something is buggy, they will hang, !!!
|
|
|
|
// !!! instead of exiting cleanly. This might wedge the buildbots. !!!
|
|
|
|
|
2013-07-15 20:28:37 -05:00
|
|
|
#[cfg(test)]
|
|
|
|
fn block_forever() { let (po, _ch) = stream::<()>(); po.recv(); }
|
|
|
|
|
2013-07-30 18:20:59 -05:00
|
|
|
#[test]
|
|
|
|
fn test_unnamed_task() {
|
2013-10-22 17:00:37 -05:00
|
|
|
use rt::test::run_in_uv_task;
|
2013-07-30 18:20:59 -05:00
|
|
|
|
2013-10-22 17:00:37 -05:00
|
|
|
do run_in_uv_task {
|
2013-07-30 18:20:59 -05:00
|
|
|
do spawn {
|
2013-11-21 19:23:21 -06:00
|
|
|
with_task_name(|name| {
|
2013-07-30 18:20:59 -05:00
|
|
|
assert!(name.is_none());
|
2013-11-21 19:23:21 -06:00
|
|
|
})
|
2013-07-30 18:20:59 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-10-05 14:01:58 -05:00
|
|
|
fn test_owned_named_task() {
|
2013-10-22 17:00:37 -05:00
|
|
|
use rt::test::run_in_uv_task;
|
2013-07-30 18:20:59 -05:00
|
|
|
|
2013-10-22 17:00:37 -05:00
|
|
|
do run_in_uv_task {
|
2013-07-30 18:20:59 -05:00
|
|
|
let mut t = task();
|
|
|
|
t.name(~"ada lovelace");
|
|
|
|
do t.spawn {
|
2013-11-21 19:23:21 -06:00
|
|
|
with_task_name(|name| {
|
2013-08-03 18:59:24 -05:00
|
|
|
assert!(name.unwrap() == "ada lovelace");
|
2013-11-21 19:23:21 -06:00
|
|
|
})
|
2013-07-30 18:20:59 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-05 14:01:58 -05:00
|
|
|
#[test]
|
|
|
|
fn test_static_named_task() {
|
2013-10-22 17:00:37 -05:00
|
|
|
use rt::test::run_in_uv_task;
|
2013-10-05 14:01:58 -05:00
|
|
|
|
2013-10-22 17:00:37 -05:00
|
|
|
do run_in_uv_task {
|
2013-10-05 14:01:58 -05:00
|
|
|
let mut t = task();
|
|
|
|
t.name("ada lovelace");
|
|
|
|
do t.spawn {
|
2013-11-21 19:23:21 -06:00
|
|
|
with_task_name(|name| {
|
2013-10-05 14:01:58 -05:00
|
|
|
assert!(name.unwrap() == "ada lovelace");
|
2013-11-21 19:23:21 -06:00
|
|
|
})
|
2013-10-05 14:01:58 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-05 14:23:24 -05:00
|
|
|
#[test]
|
|
|
|
fn test_send_named_task() {
|
2013-10-22 17:00:37 -05:00
|
|
|
use rt::test::run_in_uv_task;
|
2013-10-05 14:23:24 -05:00
|
|
|
|
2013-10-22 17:00:37 -05:00
|
|
|
do run_in_uv_task {
|
2013-10-05 14:23:24 -05:00
|
|
|
let mut t = task();
|
|
|
|
t.name("ada lovelace".into_send_str());
|
|
|
|
do t.spawn {
|
2013-11-21 19:23:21 -06:00
|
|
|
with_task_name(|name| {
|
2013-10-05 14:23:24 -05:00
|
|
|
assert!(name.unwrap() == "ada lovelace");
|
2013-11-21 19:23:21 -06:00
|
|
|
})
|
2013-10-05 14:23:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-28 18:20:41 -06:00
|
|
|
#[test]
|
|
|
|
fn test_run_basic() {
|
2013-01-22 14:38:08 -06:00
|
|
|
let (po, ch) = stream::<()>();
|
2013-11-21 18:55:40 -06:00
|
|
|
let builder = task();
|
2013-05-07 19:57:58 -05:00
|
|
|
do builder.spawn {
|
2013-01-22 14:38:08 -06:00
|
|
|
ch.send(());
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
2013-01-22 14:38:08 -06:00
|
|
|
po.recv();
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
2013-04-30 07:36:18 -05:00
|
|
|
#[cfg(test)]
|
2013-01-10 12:59:58 -06:00
|
|
|
struct Wrapper {
|
2013-05-03 15:21:33 -05:00
|
|
|
f: Option<Chan<()>>
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
|
2012-11-28 18:20:41 -06:00
|
|
|
#[test]
|
|
|
|
fn test_add_wrapper() {
|
2013-01-22 14:38:08 -06:00
|
|
|
let (po, ch) = stream::<()>();
|
2013-05-07 19:57:58 -05:00
|
|
|
let mut b0 = task();
|
|
|
|
do b0.add_wrapper |body| {
|
2013-12-03 18:44:16 -06:00
|
|
|
let ch = ch;
|
2013-11-22 01:36:52 -06:00
|
|
|
let result: proc() = proc() {
|
2012-11-28 18:20:41 -06:00
|
|
|
body();
|
2013-01-22 14:38:08 -06:00
|
|
|
ch.send(());
|
2013-03-01 16:32:37 -06:00
|
|
|
};
|
|
|
|
result
|
2012-11-28 18:20:41 -06:00
|
|
|
};
|
2013-05-07 19:57:58 -05:00
|
|
|
do b0.spawn { }
|
2013-01-22 14:38:08 -06:00
|
|
|
po.recv();
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_future_result() {
|
2013-05-07 19:57:58 -05:00
|
|
|
let mut builder = task();
|
2013-10-18 03:38:46 -05:00
|
|
|
let result = builder.future_result();
|
2013-05-07 19:57:58 -05:00
|
|
|
do builder.spawn {}
|
2013-10-11 16:20:34 -05:00
|
|
|
assert!(result.recv().is_ok());
|
2012-11-28 18:20:41 -06:00
|
|
|
|
2013-05-07 19:57:58 -05:00
|
|
|
let mut builder = task();
|
2013-10-18 03:38:46 -05:00
|
|
|
let result = builder.future_result();
|
2013-05-07 19:57:58 -05:00
|
|
|
do builder.spawn {
|
2013-10-21 15:08:31 -05:00
|
|
|
fail!();
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
2013-10-11 16:20:34 -05:00
|
|
|
assert!(result.recv().is_err());
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
2013-08-19 17:40:37 -05:00
|
|
|
#[test] #[should_fail]
|
2012-11-28 18:20:41 -06:00
|
|
|
fn test_back_to_the_future_result() {
|
2013-05-07 19:57:58 -05:00
|
|
|
let mut builder = task();
|
2013-10-18 03:38:46 -05:00
|
|
|
builder.future_result();
|
|
|
|
builder.future_result();
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_try_success() {
|
|
|
|
match do try {
|
|
|
|
~"Success!"
|
|
|
|
} {
|
|
|
|
result::Ok(~"Success!") => (),
|
2013-10-21 15:08:31 -05:00
|
|
|
_ => fail!()
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_try_fail() {
|
|
|
|
match do try {
|
2013-10-21 15:08:31 -05:00
|
|
|
fail!()
|
2012-11-28 18:20:41 -06:00
|
|
|
} {
|
2013-10-11 16:20:34 -05:00
|
|
|
result::Err(_) => (),
|
2013-10-21 15:08:31 -05:00
|
|
|
result::Ok(()) => fail!()
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-30 21:02:21 -05:00
|
|
|
#[cfg(test)]
|
|
|
|
fn get_sched_id() -> int {
|
2013-12-03 21:18:58 -06:00
|
|
|
use rt::sched::Scheduler;
|
|
|
|
let mut sched = Local::borrow(None::<Scheduler>);
|
|
|
|
sched.get().sched_id() as int
|
2013-07-30 21:02:21 -05:00
|
|
|
}
|
|
|
|
|
2012-11-28 18:20:41 -06:00
|
|
|
#[test]
|
|
|
|
fn test_spawn_sched() {
|
2013-01-22 14:38:08 -06:00
|
|
|
let (po, ch) = stream::<()>();
|
2013-04-17 01:45:29 -05:00
|
|
|
let ch = SharedChan::new(ch);
|
2012-11-28 18:20:41 -06:00
|
|
|
|
2013-01-22 14:38:08 -06:00
|
|
|
fn f(i: int, ch: SharedChan<()>) {
|
2013-07-30 21:02:21 -05:00
|
|
|
let parent_sched_id = get_sched_id();
|
2012-11-28 18:20:41 -06:00
|
|
|
|
|
|
|
do spawn_sched(SingleThreaded) {
|
2013-07-30 21:02:21 -05:00
|
|
|
let child_sched_id = get_sched_id();
|
2013-03-28 20:39:09 -05:00
|
|
|
assert!(parent_sched_id != child_sched_id);
|
2012-11-28 18:20:41 -06:00
|
|
|
|
|
|
|
if (i == 0) {
|
2013-01-22 14:38:08 -06:00
|
|
|
ch.send(());
|
2012-11-28 18:20:41 -06:00
|
|
|
} else {
|
2013-01-22 14:38:08 -06:00
|
|
|
f(i - 1, ch.clone());
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
f(10, ch);
|
2013-01-22 14:38:08 -06:00
|
|
|
po.recv();
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-01-08 21:46:12 -06:00
|
|
|
fn test_spawn_sched_childs_on_default_sched() {
|
2013-01-22 14:38:08 -06:00
|
|
|
let (po, ch) = stream();
|
2012-11-28 18:20:41 -06:00
|
|
|
|
2013-01-08 21:46:12 -06:00
|
|
|
// Assuming tests run on the default scheduler
|
2013-07-30 21:02:21 -05:00
|
|
|
let default_id = get_sched_id();
|
2012-11-28 18:20:41 -06:00
|
|
|
|
|
|
|
do spawn_sched(SingleThreaded) {
|
2013-07-30 21:02:21 -05:00
|
|
|
let parent_sched_id = get_sched_id();
|
2013-12-03 18:44:16 -06:00
|
|
|
let ch = ch;
|
2012-11-28 18:20:41 -06:00
|
|
|
do spawn {
|
2013-07-30 21:02:21 -05:00
|
|
|
let child_sched_id = get_sched_id();
|
2013-03-28 20:39:09 -05:00
|
|
|
assert!(parent_sched_id != child_sched_id);
|
2013-05-18 21:02:45 -05:00
|
|
|
assert_eq!(child_sched_id, default_id);
|
2013-01-22 14:38:08 -06:00
|
|
|
ch.send(());
|
2012-11-28 18:20:41 -06:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2013-01-22 14:38:08 -06:00
|
|
|
po.recv();
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_spawn_sched_blocking() {
|
2013-11-14 02:21:43 -06:00
|
|
|
use unstable::mutex::Mutex;
|
|
|
|
|
2013-01-11 00:36:54 -06:00
|
|
|
unsafe {
|
2012-11-28 18:20:41 -06:00
|
|
|
|
2013-01-11 00:36:54 -06:00
|
|
|
// Testing that a task in one scheduler can block in foreign code
|
|
|
|
// without affecting other schedulers
|
2013-11-21 19:23:21 -06:00
|
|
|
20u.times(|| {
|
2013-01-22 14:38:08 -06:00
|
|
|
let (start_po, start_ch) = stream();
|
|
|
|
let (fin_po, fin_ch) = stream();
|
2012-11-28 18:20:41 -06:00
|
|
|
|
2013-11-14 02:21:43 -06:00
|
|
|
let mut lock = Mutex::new();
|
2013-12-03 18:44:16 -06:00
|
|
|
let lock2 = lock.clone();
|
2012-11-28 18:20:41 -06:00
|
|
|
|
2013-01-11 00:36:54 -06:00
|
|
|
do spawn_sched(SingleThreaded) {
|
2013-12-03 18:44:16 -06:00
|
|
|
let mut lock = lock2;
|
2013-11-14 02:21:43 -06:00
|
|
|
lock.lock();
|
2012-11-28 18:20:41 -06:00
|
|
|
|
2013-06-27 10:45:24 -05:00
|
|
|
start_ch.send(());
|
2012-11-28 18:20:41 -06:00
|
|
|
|
2013-06-27 10:45:24 -05:00
|
|
|
// Block the scheduler thread
|
2013-11-14 02:21:43 -06:00
|
|
|
lock.wait();
|
|
|
|
lock.unlock();
|
2012-11-28 18:20:41 -06:00
|
|
|
|
2013-06-27 10:45:24 -05:00
|
|
|
fin_ch.send(());
|
2013-01-11 00:36:54 -06:00
|
|
|
};
|
2012-11-28 18:20:41 -06:00
|
|
|
|
2013-01-11 00:36:54 -06:00
|
|
|
// Wait until the other task has its lock
|
2013-01-22 14:38:08 -06:00
|
|
|
start_po.recv();
|
2012-11-28 18:20:41 -06:00
|
|
|
|
2013-01-22 14:38:08 -06:00
|
|
|
fn pingpong(po: &Port<int>, ch: &Chan<int>) {
|
2013-01-11 00:36:54 -06:00
|
|
|
let mut val = 20;
|
|
|
|
while val > 0 {
|
2013-01-22 14:38:08 -06:00
|
|
|
val = po.recv();
|
|
|
|
ch.send(val - 1);
|
2013-01-11 00:36:54 -06:00
|
|
|
}
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
2013-01-22 14:38:08 -06:00
|
|
|
let (setup_po, setup_ch) = stream();
|
|
|
|
let (parent_po, parent_ch) = stream();
|
2013-01-11 00:36:54 -06:00
|
|
|
do spawn {
|
2013-01-22 14:38:08 -06:00
|
|
|
let (child_po, child_ch) = stream();
|
|
|
|
setup_ch.send(child_ch);
|
|
|
|
pingpong(&child_po, &parent_ch);
|
2013-01-11 00:36:54 -06:00
|
|
|
};
|
|
|
|
|
2013-01-22 14:38:08 -06:00
|
|
|
let child_ch = setup_po.recv();
|
|
|
|
child_ch.send(20);
|
|
|
|
pingpong(&parent_po, &child_ch);
|
2013-11-14 02:21:43 -06:00
|
|
|
lock.lock();
|
|
|
|
lock.signal();
|
|
|
|
lock.unlock();
|
2013-01-22 14:38:08 -06:00
|
|
|
fin_po.recv();
|
2013-11-14 02:21:43 -06:00
|
|
|
lock.destroy();
|
2013-11-21 19:23:21 -06:00
|
|
|
})
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
2013-11-18 23:15:42 -06:00
|
|
|
fn avoid_copying_the_body(spawnfn: |v: proc()|) {
|
2013-01-22 14:38:08 -06:00
|
|
|
let (p, ch) = stream::<uint>();
|
2012-11-28 18:20:41 -06:00
|
|
|
|
|
|
|
let x = ~1;
|
2013-04-22 16:27:30 -05:00
|
|
|
let x_in_parent = ptr::to_unsafe_ptr(&*x) as uint;
|
2012-11-28 18:20:41 -06:00
|
|
|
|
2013-02-15 02:51:28 -06:00
|
|
|
do spawnfn || {
|
2013-04-22 16:27:30 -05:00
|
|
|
let x_in_child = ptr::to_unsafe_ptr(&*x) as uint;
|
2013-01-22 14:38:08 -06:00
|
|
|
ch.send(x_in_child);
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
2013-01-22 14:38:08 -06:00
|
|
|
let x_in_child = p.recv();
|
2013-05-18 21:02:45 -05:00
|
|
|
assert_eq!(x_in_parent, x_in_child);
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_avoid_copying_the_body_spawn() {
|
|
|
|
avoid_copying_the_body(spawn);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_avoid_copying_the_body_task_spawn() {
|
2013-11-21 19:23:21 -06:00
|
|
|
avoid_copying_the_body(|f| {
|
2013-11-21 18:55:40 -06:00
|
|
|
let builder = task();
|
2013-05-07 19:57:58 -05:00
|
|
|
do builder.spawn || {
|
2012-11-28 18:20:41 -06:00
|
|
|
f();
|
|
|
|
}
|
2013-11-21 19:23:21 -06:00
|
|
|
})
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_avoid_copying_the_body_try() {
|
2013-11-21 19:23:21 -06:00
|
|
|
avoid_copying_the_body(|f| {
|
2013-02-15 02:51:28 -06:00
|
|
|
do try || {
|
2012-11-28 18:20:41 -06:00
|
|
|
f()
|
|
|
|
};
|
2013-11-21 19:23:21 -06:00
|
|
|
})
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_child_doesnt_ref_parent() {
|
|
|
|
// If the child refcounts the parent task, this will stack overflow when
|
|
|
|
// climbing the task tree to dereference each ancestor. (See #1789)
|
|
|
|
// (well, it would if the constant were 8000+ - I lowered it to be more
|
|
|
|
// valgrind-friendly. try this at home, instead..!)
|
2013-03-22 16:00:15 -05:00
|
|
|
static generations: uint = 16;
|
2013-11-18 15:25:09 -06:00
|
|
|
fn child_no(x: uint) -> proc() {
|
2013-11-22 01:36:52 -06:00
|
|
|
return proc() {
|
2012-11-28 18:20:41 -06:00
|
|
|
if x < generations {
|
2013-07-26 16:20:26 -05:00
|
|
|
let mut t = task();
|
|
|
|
t.unwatched();
|
|
|
|
t.spawn(child_no(x+1));
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-07-26 16:20:26 -05:00
|
|
|
let mut t = task();
|
|
|
|
t.unwatched();
|
|
|
|
t.spawn(child_no(0));
|
2012-11-28 18:20:41 -06:00
|
|
|
}
|
|
|
|
|
2013-04-18 20:38:12 -05:00
|
|
|
#[test]
|
|
|
|
fn test_simple_newsched_spawn() {
|
2013-10-22 17:00:37 -05:00
|
|
|
use rt::test::run_in_uv_task;
|
2013-04-18 20:38:12 -05:00
|
|
|
|
2013-10-22 17:00:37 -05:00
|
|
|
do run_in_uv_task {
|
2013-11-22 01:36:52 -06:00
|
|
|
spawn(proc()())
|
2013-04-18 20:38:12 -05:00
|
|
|
}
|
|
|
|
}
|
2013-06-26 18:41:00 -05:00
|
|
|
|
2013-10-11 16:20:34 -05:00
|
|
|
#[test]
|
2013-10-27 14:12:40 -05:00
|
|
|
fn test_try_fail_message_static_str() {
|
2013-10-11 16:20:34 -05:00
|
|
|
match do try {
|
|
|
|
fail!("static string");
|
|
|
|
} {
|
2013-10-27 14:12:40 -05:00
|
|
|
Err(e) => {
|
|
|
|
type T = &'static str;
|
|
|
|
assert!(e.is::<T>());
|
|
|
|
assert_eq!(*e.move::<T>().unwrap(), "static string");
|
|
|
|
}
|
|
|
|
Ok(()) => fail!()
|
2013-10-11 16:20:34 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-10-27 14:12:40 -05:00
|
|
|
fn test_try_fail_message_owned_str() {
|
2013-10-11 16:20:34 -05:00
|
|
|
match do try {
|
|
|
|
fail!(~"owned string");
|
|
|
|
} {
|
2013-10-27 14:12:40 -05:00
|
|
|
Err(e) => {
|
|
|
|
type T = ~str;
|
|
|
|
assert!(e.is::<T>());
|
|
|
|
assert_eq!(*e.move::<T>().unwrap(), ~"owned string");
|
|
|
|
}
|
|
|
|
Ok(()) => fail!()
|
2013-10-11 16:20:34 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-10-27 14:12:40 -05:00
|
|
|
fn test_try_fail_message_any() {
|
2013-10-11 16:20:34 -05:00
|
|
|
match do try {
|
|
|
|
fail!(~413u16 as ~Any);
|
|
|
|
} {
|
2013-10-27 14:12:40 -05:00
|
|
|
Err(e) => {
|
|
|
|
type T = ~Any;
|
|
|
|
assert!(e.is::<T>());
|
|
|
|
let any = e.move::<T>().unwrap();
|
|
|
|
assert!(any.is::<u16>());
|
|
|
|
assert_eq!(*any.move::<u16>().unwrap(), 413u16);
|
|
|
|
}
|
|
|
|
Ok(()) => fail!()
|
2013-10-11 16:20:34 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-10-27 14:12:40 -05:00
|
|
|
fn test_try_fail_message_unit_struct() {
|
2013-10-11 16:20:34 -05:00
|
|
|
struct Juju;
|
|
|
|
|
|
|
|
match do try {
|
2013-10-27 14:12:40 -05:00
|
|
|
fail!(Juju)
|
2013-10-11 16:20:34 -05:00
|
|
|
} {
|
|
|
|
Err(ref e) if e.is::<Juju>() => {}
|
|
|
|
Err(_) | Ok(()) => fail!()
|
|
|
|
}
|
|
|
|
}
|