Allow fail messages to be caught, and introduce the Any trait

Some code cleanup, sorting of import blocks

Removed std::unstable::UnsafeArc's use of Either

Added run-fail tests for the new FailWithCause impls

Changed future_result and try to return Result<(), ~Any>.

- Internally, there is an enum of possible fail messages passend around.
- In case of linked failure or a string message, the ~Any gets
  lazyly allocated in future_results recv method.
- For that, future result now returns a wrapper around a Port.
- Moved and renamed task::TaskResult into rt::task::UnwindResult
  and made it an internal enum.
- Introduced a replacement typedef `type TaskResult = Result<(), ~Any>`.
This commit is contained in:
Marvin Löbel 2013-10-11 23:20:34 +02:00
parent cb5b21eba7
commit fa8e71a825
24 changed files with 913 additions and 215 deletions

View File

@ -918,7 +918,7 @@ mod tests {
let m = Mutex::new();
let m2 = m.clone();
let result: result::Result<(),()> = do task::try {
let result: result::Result<(), ~Any> = do task::try {
do m2.lock {
fail!();
}
@ -935,7 +935,7 @@ mod tests {
let m = Mutex::new();
let m2 = m.clone();
let result: result::Result<(),()> = do task::try {
let result: result::Result<(), ~Any> = do task::try {
let (p, c) = comm::stream();
do task::spawn || { // linked
let _ = p.recv(); // wait for sibling to get in the mutex
@ -963,7 +963,7 @@ mod tests {
let m2 = m.clone();
let (p, c) = comm::stream();
let result: result::Result<(),()> = do task::try {
let result: result::Result<(), ~Any> = do task::try {
let mut sibling_convos = ~[];
do 2.times {
let (p, c) = comm::stream();
@ -1272,7 +1272,7 @@ mod tests {
let x = RWLock::new();
let x2 = x.clone();
let result: result::Result<(),()> = do task::try || {
let result: result::Result<(), ~Any> = do task::try || {
do lock_rwlock_in_mode(&x2, mode1) {
fail!();
}

View File

@ -873,8 +873,7 @@ pub fn run_test(force_ignore: bool,
task.spawn(testfn_cell.take());
let task_result = result_future.recv();
let test_result = calc_result(&desc,
task_result == task::Success);
let test_result = calc_result(&desc, task_result.is_ok());
monitor_ch.send((desc.clone(), test_result));
}
}

View File

@ -640,9 +640,8 @@ fn test_install_invalid() {
pkgid.clone());
ctxt.install(pkg_src, &WhatToBuild::new(MaybeCustom, Everything));
};
// Not the best test -- doesn't test that we failed in the right way.
// Best we can do for now.
assert!(result == Err(()));
assert!(result.unwrap_err()
.to_str().contains("supplied path for package dir does not exist"));
}
#[test]

418
src/libstd/any.rs Normal file
View File

@ -0,0 +1,418 @@
// Copyright 2013 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.
//! This module implements the `Any` trait, which enables dynamic typing
//! of any type.
use cast::transmute;
use cmp::Eq;
use option::{Option, Some, None};
use to_str::ToStr;
use unstable::intrinsics::{TyDesc, get_tydesc, forget};
use util::Void;
///////////////////////////////////////////////////////////////////////////////
// TypeId
// FIXME: #9913 - Needs proper intrinsic support to work reliably cross crate
///////////////////////////////////////////////////////////////////////////////
/// `TypeId` represents a globally unique identifier for a type
pub struct TypeId {
priv t: *TyDesc
}
impl TypeId {
/// Returns the `TypeId` of the type this generic function has been instantiated with
#[inline]
pub fn of<T>() -> TypeId {
TypeId{ t: unsafe { get_tydesc::<T>() } }
}
}
impl Eq for TypeId {
#[inline]
fn eq(&self, &other: &TypeId) -> bool {
self.t == other.t
}
}
///////////////////////////////////////////////////////////////////////////////
// Any trait
///////////////////////////////////////////////////////////////////////////////
/// The `Any` trait is implemented by all types, and can be used as a trait object
/// for dynamic typing
pub trait Any {
/// Get the `TypeId` of `self`
fn get_type_id(&self) -> TypeId {
TypeId::of::<Self>()
}
/// Get a void pointer to `self`
fn as_void_ptr(&self) -> *Void {
self as *Self as *Void
}
/// Get a mutable void pointer to `self`
fn as_mut_void_ptr(&mut self) -> *mut Void {
self as *mut Self as *mut Void
}
}
impl<T> Any for T {}
///////////////////////////////////////////////////////////////////////////////
// Extension methods for Any trait objects.
// Implemented as three extension traits so that generics work.
///////////////////////////////////////////////////////////////////////////////
/// Extension methods for a referenced `Any` trait object
pub trait AnyRefExt<'self> {
/// Returns true if the boxed type is the same as `T`
fn is<T>(self) -> bool;
/// Returns some reference to the boxed value if it is of type `T`, or
/// `None` if it isn't.
fn as_ref<T>(self) -> Option<&'self T>;
}
impl<'self> AnyRefExt<'self> for &'self Any {
#[inline]
fn is<T>(self) -> bool {
// Get TypeId of the type this function is instantiated with
let t = TypeId::of::<T>();
// Get TypeId of the type in the trait object
let boxed = self.get_type_id();
// Compare both TypeIds on equality
t == boxed
}
#[inline]
fn as_ref<T>(self) -> Option<&'self T> {
if self.is::<T>() {
Some(unsafe { transmute(self.as_void_ptr()) })
} else {
None
}
}
}
/// Extension methods for a mutable referenced `Any` trait object
pub trait AnyMutRefExt<'self> {
/// Returns some mutable reference to the boxed value if it is of type `T`, or
/// `None` if it isn't.
fn as_mut<T>(self) -> Option<&'self mut T>;
}
impl<'self> AnyMutRefExt<'self> for &'self mut Any {
#[inline]
fn as_mut<T>(self) -> Option<&'self mut T> {
if self.is::<T>() {
Some(unsafe { transmute(self.as_mut_void_ptr()) })
} else {
None
}
}
}
/// Extension methods for a owning `Any` trait object
pub trait AnyOwnExt {
/// Returns the boxed value if it is of type `T`, or
/// `None` if it isn't.
fn move<T>(self) -> Option<~T>;
}
impl AnyOwnExt for ~Any {
#[inline]
fn move<T>(self) -> Option<~T> {
if self.is::<T>() {
unsafe {
// Extract the pointer to the boxed value, temporary alias with self
let ptr: ~T = transmute(self.as_void_ptr());
// Prevent destructor on self being run
forget(self);
Some(ptr)
}
} else {
None
}
}
}
///////////////////////////////////////////////////////////////////////////////
// Trait implementations
///////////////////////////////////////////////////////////////////////////////
impl ToStr for ~Any {
fn to_str(&self) -> ~str { ~"~Any" }
}
impl<'self> ToStr for &'self Any {
fn to_str(&self) -> ~str { ~"&Any" }
}
#[cfg(test)]
mod tests {
use super::*;
use super::AnyRefExt;
use option::{Some, None};
#[deriving(Eq)]
struct Test;
static TEST: &'static str = "Test";
#[test]
fn type_id() {
let (a, b, c) = (TypeId::of::<uint>(), TypeId::of::<&str>(), TypeId::of::<Test>());
let (d, e, f) = (TypeId::of::<uint>(), TypeId::of::<&str>(), TypeId::of::<Test>());
assert!(a != b);
assert!(a != c);
assert!(b != c);
assert_eq!(a, d);
assert_eq!(b, e);
assert_eq!(c, f);
}
#[test]
fn any_as_void_ptr() {
let (a, b, c) = (~5u as ~Any, ~TEST as ~Any, ~Test as ~Any);
let a_r: &Any = a;
let b_r: &Any = b;
let c_r: &Any = c;
assert_eq!(a.as_void_ptr(), a_r.as_void_ptr());
assert_eq!(b.as_void_ptr(), b_r.as_void_ptr());
assert_eq!(c.as_void_ptr(), c_r.as_void_ptr());
let (a, b, c) = (@5u as @Any, @TEST as @Any, @Test as @Any);
let a_r: &Any = a;
let b_r: &Any = b;
let c_r: &Any = c;
assert_eq!(a.as_void_ptr(), a_r.as_void_ptr());
assert_eq!(b.as_void_ptr(), b_r.as_void_ptr());
assert_eq!(c.as_void_ptr(), c_r.as_void_ptr());
let (a, b, c) = (&5u as &Any, &TEST as &Any, &Test as &Any);
let a_r: &Any = a;
let b_r: &Any = b;
let c_r: &Any = c;
assert_eq!(a.as_void_ptr(), a_r.as_void_ptr());
assert_eq!(b.as_void_ptr(), b_r.as_void_ptr());
assert_eq!(c.as_void_ptr(), c_r.as_void_ptr());
let mut x = Test;
let mut y: &'static str = "Test";
let (a, b, c) = (&mut 5u as &mut Any,
&mut y as &mut Any,
&mut x as &mut Any);
let a_r: &Any = a;
let b_r: &Any = b;
let c_r: &Any = c;
assert_eq!(a.as_void_ptr(), a_r.as_void_ptr());
assert_eq!(b.as_void_ptr(), b_r.as_void_ptr());
assert_eq!(c.as_void_ptr(), c_r.as_void_ptr());
let (a, b, c) = (5u, "hello", Test);
let (a_r, b_r, c_r) = (&a as &Any, &b as &Any, &c as &Any);
assert_eq!(a.as_void_ptr(), a_r.as_void_ptr());
assert_eq!(b.as_void_ptr(), b_r.as_void_ptr());
assert_eq!(c.as_void_ptr(), c_r.as_void_ptr());
}
#[test]
fn any_as_mut_void_ptr() {
let y: &'static str = "Test";
let mut a = ~5u as ~Any;
let mut b = ~y as ~Any;
let mut c = ~Test as ~Any;
let a_ptr = a.as_mut_void_ptr();
let b_ptr = b.as_mut_void_ptr();
let c_ptr = c.as_mut_void_ptr();
let a_r: &mut Any = a;
let b_r: &mut Any = b;
let c_r: &mut Any = c;
assert_eq!(a_ptr, a_r.as_mut_void_ptr());
assert_eq!(b_ptr, b_r.as_mut_void_ptr());
assert_eq!(c_ptr, c_r.as_mut_void_ptr());
let mut x = Test;
let mut y: &'static str = "Test";
let a = &mut 5u as &mut Any;
let b = &mut y as &mut Any;
let c = &mut x as &mut Any;
let a_ptr = a.as_mut_void_ptr();
let b_ptr = b.as_mut_void_ptr();
let c_ptr = c.as_mut_void_ptr();
let a_r: &mut Any = a;
let b_r: &mut Any = b;
let c_r: &mut Any = c;
assert_eq!(a_ptr, a_r.as_mut_void_ptr());
assert_eq!(b_ptr, b_r.as_mut_void_ptr());
assert_eq!(c_ptr, c_r.as_mut_void_ptr());
let y: &'static str = "Test";
let mut a = 5u;
let mut b = y;
let mut c = Test;
let a_ptr = a.as_mut_void_ptr();
let b_ptr = b.as_mut_void_ptr();
let c_ptr = c.as_mut_void_ptr();
let (a_r, b_r, c_r) = (&mut a as &mut Any, &mut b as &mut Any, &mut c as &mut Any);
assert_eq!(a_ptr, a_r.as_mut_void_ptr());
assert_eq!(b_ptr, b_r.as_mut_void_ptr());
assert_eq!(c_ptr, c_r.as_mut_void_ptr());
}
#[test]
fn any_referenced() {
let (a, b, c) = (&5u as &Any, &TEST as &Any, &Test as &Any);
assert!(a.is::<uint>());
assert!(!b.is::<uint>());
assert!(!c.is::<uint>());
assert!(!a.is::<&'static str>());
assert!(b.is::<&'static str>());
assert!(!c.is::<&'static str>());
assert!(!a.is::<Test>());
assert!(!b.is::<Test>());
assert!(c.is::<Test>());
}
#[test]
fn any_owning() {
let (a, b, c) = (~5u as ~Any, ~TEST as ~Any, ~Test as ~Any);
assert!(a.is::<uint>());
assert!(!b.is::<uint>());
assert!(!c.is::<uint>());
assert!(!a.is::<&'static str>());
assert!(b.is::<&'static str>());
assert!(!c.is::<&'static str>());
assert!(!a.is::<Test>());
assert!(!b.is::<Test>());
assert!(c.is::<Test>());
}
#[test]
fn any_managed() {
let (a, b, c) = (@5u as @Any, @TEST as @Any, @Test as @Any);
assert!(a.is::<uint>());
assert!(!b.is::<uint>());
assert!(!c.is::<uint>());
assert!(!a.is::<&'static str>());
assert!(b.is::<&'static str>());
assert!(!c.is::<&'static str>());
assert!(!a.is::<Test>());
assert!(!b.is::<Test>());
assert!(c.is::<Test>());
}
#[test]
fn any_as_ref() {
let a = &5u as &Any;
match a.as_ref::<uint>() {
Some(&5) => {}
x => fail!("Unexpected value {:?}", x)
}
match a.as_ref::<Test>() {
None => {}
x => fail!("Unexpected value {:?}", x)
}
}
#[test]
fn any_as_mut() {
let mut a = 5u;
let mut b = ~7u;
let a_r = &mut a as &mut Any;
let tmp: &mut uint = b;
let b_r = tmp as &mut Any;
match a_r.as_mut::<uint>() {
Some(x) => {
assert_eq!(*x, 5u);
*x = 612;
}
x => fail!("Unexpected value {:?}", x)
}
match b_r.as_mut::<uint>() {
Some(x) => {
assert_eq!(*x, 7u);
*x = 413;
}
x => fail!("Unexpected value {:?}", x)
}
match a_r.as_mut::<Test>() {
None => (),
x => fail!("Unexpected value {:?}", x)
}
match b_r.as_mut::<Test>() {
None => (),
x => fail!("Unexpected value {:?}", x)
}
match a_r.as_mut::<uint>() {
Some(&612) => {}
x => fail!("Unexpected value {:?}", x)
}
match b_r.as_mut::<uint>() {
Some(&413) => {}
x => fail!("Unexpected value {:?}", x)
}
}
#[test]
fn any_move() {
let a = ~8u as ~Any;
let b = ~Test as ~Any;
assert_eq!(a.move(), Some(~8u));
assert_eq!(b.move(), Some(~Test));
let a = ~8u as ~Any;
let b = ~Test as ~Any;
assert_eq!(a.move(), None::<~Test>);
assert_eq!(b.move(), None::<~uint>);
}
}

View File

@ -43,6 +43,8 @@ pub use iter::range;
pub use rt::io::stdio::{print, println};
// Reexported types and traits
pub use any::{Any, AnyOwnExt, AnyRefExt, AnyMutRefExt};
pub use ascii::{Ascii, AsciiCast, OwnedAsciiCast, AsciiStr, ToBytesConsume};
pub use bool::Bool;
pub use c_str::ToCStr;

View File

@ -107,7 +107,7 @@ mod test {
use option::*;
use super::super::mem::*;
use rt::test::*;
use super::super::{PreviousIoError, io_error, io_error};
use super::super::{PreviousIoError, io_error};
#[test]
fn test_option_writer() {

View File

@ -145,10 +145,10 @@ impl Listener {
#[cfg(test)]
mod test {
use super::*;
use libc;
use rt::io::timer;
use rt::io;
use super::*;
// kill is only available on Unixes
#[cfg(unix)]
@ -206,6 +206,7 @@ mod test {
#[cfg(windows)]
#[test]
fn test_io_signal_invalid_signum() {
use rt::io;
let mut s = Listener::new();
let mut called = false;
do io::io_error::cond.trap(|_| {

View File

@ -152,14 +152,15 @@ There are two known issues with the current scheme for exit code propagation.
use cast;
use cell::Cell;
use either::{Either, Left, Right};
use option::{Option, Some, None};
use prelude::*;
use rt::task::Task;
use rt::task::UnwindReasonLinked;
use rt::task::{UnwindResult, Failure};
use task::spawn::Taskgroup;
use to_bytes::IterBytes;
use unstable::atomics::{AtomicUint, Relaxed};
use unstable::sync::{UnsafeArc, LittleLock};
use unstable::sync::{UnsafeArc, UnsafeArcSelf, UnsafeArcT, LittleLock};
use util;
static KILLED_MSG: &'static str = "killed by linked failure";
@ -222,7 +223,7 @@ pub struct Death {
priv watching_parent: Option<KillHandle>,
// Action to be done with the exit code. If set, also makes the task wait
// until all its watched children exit before collecting the status.
on_exit: Option<~fn(bool)>,
on_exit: Option<~fn(UnwindResult)>,
// nesting level counter for task::unkillable calls (0 == killable).
priv unkillable: int,
// nesting level counter for unstable::atomically calls (0 == can deschedule).
@ -478,7 +479,7 @@ impl KillHandle {
match self.try_unwrap() {
// Couldn't unwrap; children still alive. Reparent entire handle as
// our own tombstone, to be unwrapped later.
Left(this) => {
UnsafeArcSelf(this) => {
let this = Cell::new(this); // :(
do add_lazy_tombstone(parent) |other_tombstones| {
let this = Cell::new(this.take()); // :(
@ -494,13 +495,15 @@ impl KillHandle {
}
}
}
// Whether or not all children exited, one or more already failed.
Right(KillHandleInner { any_child_failed: true, _ }) => {
UnsafeArcT(KillHandleInner { any_child_failed: true, _ }) => {
parent.notify_immediate_failure();
}
// All children exited, but some left behind tombstones that we
// don't want to wait on now. Give them to our parent.
Right(KillHandleInner { any_child_failed: false,
UnsafeArcT(KillHandleInner { any_child_failed: false,
child_tombstones: Some(f), _ }) => {
let f = Cell::new(f); // :(
do add_lazy_tombstone(parent) |other_tombstones| {
@ -513,8 +516,9 @@ impl KillHandle {
}
}
}
// All children exited, none failed. Nothing to do!
Right(KillHandleInner { any_child_failed: false,
UnsafeArcT(KillHandleInner { any_child_failed: false,
child_tombstones: None, _ }) => { }
}
@ -562,7 +566,7 @@ impl Death {
}
/// Collect failure exit codes from children and propagate them to a parent.
pub fn collect_failure(&mut self, mut success: bool, group: Option<Taskgroup>) {
pub fn collect_failure(&mut self, result: UnwindResult, group: Option<Taskgroup>) {
// This may run after the task has already failed, so even though the
// task appears to need to be killed, the scheduler should not fail us
// when we block to unwrap.
@ -576,19 +580,27 @@ impl Death {
// FIXME(#8192): Doesn't work with "let _ = ..."
{ use util; util::ignore(group); }
let mut success = result.is_success();
let mut result = Cell::new(result);
// Step 1. Decide if we need to collect child failures synchronously.
do self.on_exit.take().map |on_exit| {
if success {
// We succeeded, but our children might not. Need to wait for them.
let mut inner = self.kill_handle.take_unwrap().unwrap();
if inner.any_child_failed {
success = false;
} else {
// Lockless access to tombstones protected by unwrap barrier.
success = inner.child_tombstones.take().map_default(true, |f| f());
}
if !success {
result = Cell::new(Failure(UnwindReasonLinked));
}
on_exit(success);
}
on_exit(result.take());
};
// Step 2. Possibly alert possibly-watching parent to failure status.

View File

@ -66,12 +66,13 @@ use ptr::RawPtr;
use rt::local::Local;
use rt::sched::{Scheduler, Shutdown};
use rt::sleeper_list::SleeperList;
use rt::task::UnwindResult;
use rt::task::{Task, SchedTask, GreenTask, Sched};
use rt::uv::uvio::UvEventLoop;
use unstable::atomics::{AtomicInt, AtomicBool, SeqCst};
use unstable::sync::UnsafeArc;
use vec;
use vec::{OwnedVector, MutableVector, ImmutableVector};
use vec;
use self::thread::Thread;
use self::work_queue::WorkQueue;
@ -343,7 +344,7 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
// When the main task exits, after all the tasks in the main
// task tree, shut down the schedulers and set the exit code.
let handles = Cell::new(handles);
let on_exit: ~fn(bool) = |exit_success| {
let on_exit: ~fn(UnwindResult) = |exit_success| {
unsafe {
assert!(!(*exited_already.get()).swap(true, SeqCst),
"the runtime already exited");
@ -355,7 +356,7 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
}
unsafe {
let exit_code = if exit_success {
let exit_code = if exit_success.is_success() {
use rt::util;
// If we're exiting successfully, then return the global

View File

@ -915,7 +915,6 @@ mod test {
use rt::test::*;
use unstable::run_in_bare_thread;
use borrow::to_uint;
use rt::local::*;
use rt::sched::{Scheduler};
use cell::Cell;
use rt::thread::Thread;
@ -923,6 +922,7 @@ mod test {
use rt::basic;
use rt::util;
use option::{Some};
use rt::task::UnwindResult;
#[test]
fn trivial_run_in_newsched_task_test() {
@ -1007,7 +1007,7 @@ mod test {
assert!(Task::on_appropriate_sched());
};
let on_exit: ~fn(bool) = |exit_status| rtassert!(exit_status);
let on_exit: ~fn(UnwindResult) = |exit_status| rtassert!(exit_status.is_success());
task.death.on_exit = Some(on_exit);
sched.bootstrap(task);

View File

@ -13,29 +13,31 @@
//! local storage, and logging. Even a 'freestanding' Rust would likely want
//! to implement this.
use super::local_heap::LocalHeap;
use prelude::*;
use borrow;
use cast::transmute;
use cell::Cell;
use cleanup;
use local_data;
use libc::{c_void, uintptr_t, c_char, size_t};
use prelude::*;
use local_data;
use option::{Option, Some, None};
use rt::borrowck;
use rt::borrowck::BorrowRecord;
use rt::borrowck;
use rt::context::Context;
use rt::context;
use rt::env;
use rt::io::Writer;
use rt::kill::Death;
use rt::local::Local;
use rt::logging::StdErrLogger;
use super::local_heap::LocalHeap;
use rt::sched::{Scheduler, SchedHandle};
use rt::stack::{StackSegment, StackPool};
use rt::context;
use rt::context::Context;
use unstable::finally::Finally;
use task::spawn::Taskgroup;
use cell::Cell;
use send_str::SendStr;
use task::spawn::Taskgroup;
use unstable::finally::Finally;
// The Task struct represents all state associated with a rust
// task. There are at this point two primary "subtypes" of task,
@ -85,8 +87,61 @@ pub enum SchedHome {
pub struct GarbageCollector;
pub struct LocalStorage(Option<local_data::Map>);
/// Represents the reason for the current unwinding process
pub enum UnwindResult {
/// The task is ending successfully
Success,
/// The Task is failing with reason `UnwindReason`
Failure(UnwindReason),
}
impl UnwindResult {
/// Returns `true` if this `UnwindResult` is a failure
#[inline]
pub fn is_failure(&self) -> bool {
match *self {
Success => false,
Failure(_) => true
}
}
/// Returns `true` if this `UnwindResult` is a success
#[inline]
pub fn is_success(&self) -> bool {
match *self {
Success => true,
Failure(_) => false
}
}
}
/// Represents the cause of a task failure
#[deriving(ToStr)]
pub enum UnwindReason {
/// Failed with a string message
UnwindReasonStr(SendStr),
/// Failed with an `~Any`
UnwindReasonAny(~Any),
/// Failed because of linked failure
UnwindReasonLinked
}
pub struct Unwinder {
unwinding: bool,
cause: Option<UnwindReason>
}
impl Unwinder {
fn to_unwind_result(&mut self) -> UnwindResult {
if self.unwinding {
Failure(self.cause.take().unwrap())
} else {
Success
}
}
}
impl Task {
@ -135,7 +190,7 @@ impl Task {
gc: GarbageCollector,
storage: LocalStorage(None),
logger: StdErrLogger::new(),
unwinder: Unwinder { unwinding: false },
unwinder: Unwinder { unwinding: false, cause: None },
taskgroup: None,
death: Death::new(),
destroyed: false,
@ -170,7 +225,7 @@ impl Task {
gc: GarbageCollector,
storage: LocalStorage(None),
logger: StdErrLogger::new(),
unwinder: Unwinder { unwinding: false },
unwinder: Unwinder { unwinding: false, cause: None },
taskgroup: None,
death: Death::new(),
destroyed: false,
@ -193,7 +248,7 @@ impl Task {
gc: GarbageCollector,
storage: LocalStorage(None),
logger: StdErrLogger::new(),
unwinder: Unwinder { unwinding: false },
unwinder: Unwinder { unwinding: false, cause: None },
taskgroup: None,
// FIXME(#7544) make watching optional
death: self.death.new_child(),
@ -284,7 +339,7 @@ impl Task {
// the unkillable counter is set. This is necessary for when the
// taskgroup destruction code drops references on KillHandles, which
// might require using unkillable (to synchronize with an unwrapper).
self.death.collect_failure(!self.unwinder.unwinding, self.taskgroup.take());
self.death.collect_failure(self.unwinder.to_unwind_result(), self.taskgroup.take());
self.destroyed = true;
}
@ -469,10 +524,11 @@ impl Unwinder {
}
}
pub fn begin_unwind(&mut self) -> ! {
pub fn begin_unwind(&mut self, cause: UnwindReason) -> ! {
#[fixed_stack_segment]; #[inline(never)];
self.unwinding = true;
self.cause = Some(cause);
unsafe {
rust_begin_unwind(UNWIND_TOKEN);
return transmute(());
@ -561,36 +617,55 @@ pub extern "C" fn rust_stack_exhausted() {
}
/// This is the entry point of unwinding for things like lang items and such.
/// The arguments are normally generated by the compiler.
/// The arguments are normally generated by the compiler, and need to
/// have static lifetimes.
pub fn begin_unwind(msg: *c_char, file: *c_char, line: size_t) -> ! {
use c_str::CString;
use cast::transmute;
#[inline]
fn static_char_ptr(p: *c_char) -> &'static str {
let s = unsafe { CString::new(p, false) };
match s.as_str() {
Some(s) => unsafe { transmute::<&str, &'static str>(s) },
None => rtabort!("message wasn't utf8?")
}
}
let msg = static_char_ptr(msg);
let file = static_char_ptr(file);
begin_unwind_reason(UnwindReasonStr(msg.into_send_str()), file, line as uint)
}
/// This is the entry point of unwinding for fail!() and assert!().
pub fn begin_unwind_reason(reason: UnwindReason, file: &'static str, line: uint) -> ! {
use rt::in_green_task_context;
use rt::task::Task;
use rt::local::Local;
use str::Str;
use c_str::CString;
use unstable::intrinsics;
unsafe {
let msg = CString::new(msg, false);
let file = CString::new(file, false);
let msg = match msg.as_str() {
Some(s) => s, None => rtabort!("message wasn't utf8?")
// Be careful not to allocate in this block, if we're failing we may
// have been failing due to a lack of memory in the first place...
let task: *mut Task;
{
let msg = match reason {
UnwindReasonStr(ref s) => s.as_slice(),
UnwindReasonAny(_) => "~Any",
UnwindReasonLinked => "linked failure",
};
if !in_green_task_context() {
match file.as_str() {
Some(file) => {
rterrln!("failed in non-task context at '{}', {}:{}",
msg, file, line as int);
}
None => rterrln!("failed in non-task context at '{}'", msg)
}
msg, file, line);
intrinsics::abort();
}
// Be careful not to allocate in this block, if we're failing we may
// have been failing due to a lack of memory in the first place...
let task: *mut Task = Local::unsafe_borrow();
task = Local::unsafe_borrow();
let n = (*task).name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
// XXX: this should no get forcibly printed to the console, this should
@ -600,16 +675,15 @@ pub fn begin_unwind(msg: *c_char, file: *c_char, line: size_t) -> ! {
// reasons. This will cause serious trouble if the task is failing
// due to mismanagment of its own kill flag, so calling our own
// logger in its current state is a bit of a problem.
match file.as_str() {
Some(file) => {
rterrln!("task '{}' failed at '{}', {}:{}", n, msg, file, line);
}
None => rterrln!("task '{}' failed at '{}'", n, msg),
}
if (*task).unwinder.unwinding {
rtabort!("unwinding again");
}
(*task).unwinder.begin_unwind();
}
(*task).unwinder.begin_unwind(reason);
}
}

View File

@ -8,30 +8,32 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rand;
use rand::Rng;
use os;
use libc;
use option::{Some, None};
use path::Path;
use super::io::net::ip::{SocketAddr, Ipv4Addr, Ipv6Addr};
use cell::Cell;
use clone::Clone;
use container::Container;
use iter::{Iterator, range};
use super::io::net::ip::{SocketAddr, Ipv4Addr, Ipv6Addr};
use vec::{OwnedVector, MutableVector, ImmutableVector};
use libc;
use option::{Some, None};
use os;
use path::GenericPath;
use path::Path;
use rand::Rng;
use rand;
use result::{Result, Ok, Err};
use rt::basic;
use rt::sched::Scheduler;
use rt::comm::oneshot;
use rt::rtio::EventLoop;
use unstable::{run_in_bare_thread};
use rt::thread::Thread;
use rt::sched::Scheduler;
use rt::sleeper_list::SleeperList;
use rt::task::Task;
use rt::task::UnwindResult;
use rt::thread::Thread;
use rt::uv::uvio::UvEventLoop;
use rt::work_queue::WorkQueue;
use rt::sleeper_list::SleeperList;
use rt::comm::oneshot;
use result::{Result, Ok, Err};
use unstable::{run_in_bare_thread};
use vec::{OwnedVector, MutableVector, ImmutableVector};
pub fn new_test_uv_sched() -> Scheduler {
@ -85,9 +87,9 @@ pub fn run_in_uv_task_core(f: ~fn()) {
let mut sched = ~new_test_uv_sched();
let exit_handle = Cell::new(sched.make_handle());
let on_exit: ~fn(bool) = |exit_status| {
let on_exit: ~fn(UnwindResult) = |exit_status| {
exit_handle.take().send(Shutdown);
rtassert!(exit_status);
rtassert!(exit_status.is_success());
};
let mut task = ~Task::new_root(&mut sched.stack_pool, None, f);
task.death.on_exit = Some(on_exit);
@ -96,15 +98,14 @@ pub fn run_in_uv_task_core(f: ~fn()) {
}
pub fn run_in_newsched_task_core(f: ~fn()) {
use rt::sched::Shutdown;
let mut sched = ~new_test_sched();
let exit_handle = Cell::new(sched.make_handle());
let on_exit: ~fn(bool) = |exit_status| {
let on_exit: ~fn(UnwindResult) = |exit_status| {
exit_handle.take().send(Shutdown);
rtassert!(exit_status);
rtassert!(exit_status.is_success());
};
let mut task = ~Task::new_root(&mut sched.stack_pool, None, f);
task.death.on_exit = Some(on_exit);
@ -248,14 +249,14 @@ pub fn run_in_mt_newsched_task(f: ~fn()) {
}
let handles = Cell::new(handles);
let on_exit: ~fn(bool) = |exit_status| {
let on_exit: ~fn(UnwindResult) = |exit_status| {
let mut handles = handles.take();
// Tell schedulers to exit
for handle in handles.mut_iter() {
handle.send(Shutdown);
}
rtassert!(exit_status);
rtassert!(exit_status.is_success());
};
let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool, None, f.take());
main_task.death.on_exit = Some(on_exit);
@ -323,7 +324,7 @@ pub fn spawntask_try(f: ~fn()) -> Result<(),()> {
let (port, chan) = oneshot();
let chan = Cell::new(chan);
let on_exit: ~fn(bool) = |exit_status| chan.take().send(exit_status);
let on_exit: ~fn(UnwindResult) = |exit_status| chan.take().send(exit_status);
let mut new_task = Task::build_root(None, f);
new_task.death.on_exit = Some(on_exit);
@ -331,7 +332,7 @@ pub fn spawntask_try(f: ~fn()) -> Result<(),()> {
Scheduler::run_task(new_task);
let exit_status = port.recv();
if exit_status { Ok(()) } else { Err(()) }
if exit_status.is_success() { Ok(()) } else { Err(()) }
}

View File

@ -1899,6 +1899,7 @@ fn test_simple_homed_udp_io_bind_then_move_task_then_home_and_close() {
use rt::thread::Thread;
use rt::task::Task;
use rt::sched::{Shutdown, TaskFromFriend};
use rt::task::UnwindResult;
do run_in_bare_thread {
let sleepers = SleeperList::new();
let work_queue1 = WorkQueue::new();
@ -1916,10 +1917,10 @@ fn test_simple_homed_udp_io_bind_then_move_task_then_home_and_close() {
let handle2 = Cell::new(sched2.make_handle());
let tasksFriendHandle = Cell::new(sched2.make_handle());
let on_exit: ~fn(bool) = |exit_status| {
let on_exit: ~fn(UnwindResult) = |exit_status| {
handle1.take().send(Shutdown);
handle2.take().send(Shutdown);
rtassert!(exit_status);
rtassert!(exit_status.is_success());
};
let test_function: ~fn() = || {
@ -1978,6 +1979,7 @@ fn test_simple_homed_udp_io_bind_then_move_handle_then_home_and_close() {
use rt::task::Task;
use rt::comm::oneshot;
use rt::sched::Shutdown;
use rt::task::UnwindResult;
do run_in_bare_thread {
let sleepers = SleeperList::new();
let work_queue1 = WorkQueue::new();
@ -2017,10 +2019,10 @@ fn test_simple_homed_udp_io_bind_then_move_handle_then_home_and_close() {
*/
};
let on_exit: ~fn(bool) = |exit| {
let on_exit: ~fn(UnwindResult) = |exit| {
handle1.take().send(Shutdown);
handle2.take().send(Shutdown);
rtassert!(exit);
rtassert!(exit.is_success());
};
let task1 = Cell::new(~Task::new_root(&mut sched1.stack_pool, None, body1));
@ -2088,6 +2090,7 @@ fn test_simple_tcp_server_and_client_on_diff_threads() {
use rt::thread::Thread;
use rt::task::Task;
use rt::sched::{Shutdown};
use rt::task::UnwindResult;
do run_in_bare_thread {
let sleepers = SleeperList::new();
@ -2108,14 +2111,14 @@ fn test_simple_tcp_server_and_client_on_diff_threads() {
let server_handle = Cell::new(server_sched.make_handle());
let client_handle = Cell::new(client_sched.make_handle());
let server_on_exit: ~fn(bool) = |exit_status| {
let server_on_exit: ~fn(UnwindResult) = |exit_status| {
server_handle.take().send(Shutdown);
rtassert!(exit_status);
rtassert!(exit_status.is_success());
};
let client_on_exit: ~fn(bool) = |exit_status| {
let client_on_exit: ~fn(UnwindResult) = |exit_status| {
client_handle.take().send(Shutdown);
rtassert!(exit_status);
rtassert!(exit_status.is_success());
};
let server_fn: ~fn() = || {

View File

@ -426,7 +426,7 @@ mod tests {
os::close(pipe_err.out);
do spawn {
writeclose(pipe_in.out, ~"test");
writeclose(pipe_in.out, "test");
}
let actual = readclose(pipe_out.input);
readclose(pipe_err.input);

View File

@ -151,6 +151,7 @@ pub mod clone;
pub mod hash;
pub mod container;
pub mod default;
pub mod any;
/* Common data structures */
@ -213,15 +214,16 @@ mod std {
pub use clone;
pub use cmp;
pub use condition;
pub use option;
pub use fmt;
pub use kinds;
pub use local_data;
pub use logging;
pub use sys;
pub use unstable;
pub use str;
pub use os;
pub use fmt;
pub use to_bytes;
pub use logging;
pub use option;
pub use os;
pub use str;
pub use sys;
pub use to_bytes;
pub use to_str;
pub use unstable;
}

View File

@ -12,41 +12,55 @@
#[allow(missing_doc)];
use c_str::ToCStr;
use libc::size_t;
use libc;
use any::Any;
use kinds::Send;
use rt::task::{UnwindReasonStr, UnwindReasonAny};
use rt::task;
use send_str::{SendStr, IntoSendStr};
/// Trait for initiating task failure.
/// Trait for initiating task failure with a sendable cause.
pub trait FailWithCause {
/// Fail the current task, taking ownership of `cause`
/// Fail the current task with `cause`.
fn fail_with(cause: Self, file: &'static str, line: uint) -> !;
}
impl FailWithCause for ~str {
fn fail_with(cause: ~str, file: &'static str, line: uint) -> ! {
do cause.with_c_str |msg_buf| {
do file.with_c_str |file_buf| {
task::begin_unwind(msg_buf, file_buf, line as libc::size_t)
}
}
task::begin_unwind_reason(UnwindReasonStr(cause.into_send_str()), file, line)
}
}
impl FailWithCause for &'static str {
fn fail_with(cause: &'static str, file: &'static str, line: uint) -> ! {
do cause.with_c_str |msg_buf| {
do file.with_c_str |file_buf| {
task::begin_unwind(msg_buf, file_buf, line as libc::size_t)
task::begin_unwind_reason(UnwindReasonStr(cause.into_send_str()), file, line)
}
}
impl FailWithCause for SendStr {
fn fail_with(cause: SendStr, file: &'static str, line: uint) -> ! {
task::begin_unwind_reason(UnwindReasonStr(cause), file, line)
}
}
impl FailWithCause for ~Any {
fn fail_with(cause: ~Any, file: &'static str, line: uint) -> ! {
task::begin_unwind_reason(UnwindReasonAny(cause), file, line)
}
}
impl<T: Any + Send + 'static> FailWithCause for ~T {
fn fail_with(cause: ~T, file: &'static str, line: uint) -> ! {
task::begin_unwind_reason(UnwindReasonAny(cause as ~Any), file, line)
}
}
#[cfg(test)]
mod tests {
use super::*;
use any::Any;
use cast;
use sys::*;
use send_str::IntoSendStr;
#[test]
fn synthesize_closure() {
@ -79,4 +93,16 @@ mod tests {
#[test]
#[should_fail]
fn fail_owned() { FailWithCause::fail_with(~"cause", file!(), line!()) }
#[test]
#[should_fail]
fn fail_send() { FailWithCause::fail_with("cause".into_send_str(), file!(), line!()) }
#[test]
#[should_fail]
fn fail_any() { FailWithCause::fail_with(~612_u16 as ~Any, file!(), line!()) }
#[test]
#[should_fail]
fn fail_any_wrap() { FailWithCause::fail_with(~413_u16, file!(), line!()) }
}

View File

@ -56,38 +56,68 @@
use prelude::*;
use cell::Cell;
use comm::{stream, Chan, GenericChan, GenericPort, Port};
use result::Result;
use result;
use comm::{stream, Chan, GenericChan, GenericPort, Port, Peekable};
use result::{Result, Ok, Err};
use rt::in_green_task_context;
use rt::local::Local;
use rt::task::{UnwindReasonAny, UnwindReasonLinked, UnwindReasonStr};
use rt::task::{UnwindResult, Success, Failure};
use send_str::{SendStr, IntoSendStr};
use unstable::finally::Finally;
use util;
use send_str::{SendStr, IntoSendStr};
#[cfg(test)] use any::Any;
#[cfg(test)] use cast;
#[cfg(test)] use comm::SharedChan;
#[cfg(test)] use comm;
#[cfg(test)] use ptr;
#[cfg(test)] use result;
#[cfg(test)] use task;
pub mod spawn;
/**
* 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.
*/
#[deriving(Eq)]
pub enum TaskResult {
Success,
Failure,
/// 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 LinkedFailure;
#[inline]
fn wrap_as_any(res: UnwindResult) -> TaskResult {
match res {
Success => Ok(()),
Failure(UnwindReasonStr(s)) => Err(~s as ~Any),
Failure(UnwindReasonAny(a)) => Err(a),
Failure(UnwindReasonLinked) => Err(~LinkedFailure as ~Any)
}
}
pub struct TaskResultPort {
priv port: Port<UnwindResult>
}
impl GenericPort<TaskResult> for TaskResultPort {
#[inline]
fn recv(&self) -> TaskResult {
wrap_as_any(self.port.recv())
}
#[inline]
fn try_recv(&self) -> Option<TaskResult> {
self.port.try_recv().map(wrap_as_any)
}
}
impl Peekable<TaskResult> for TaskResultPort {
#[inline]
fn peek(&self) -> bool { self.port.peek() }
}
/// Scheduler modes
@ -148,7 +178,7 @@ pub struct TaskOpts {
priv supervised: bool,
priv watched: bool,
priv indestructible: bool,
priv notify_chan: Option<Chan<TaskResult>>,
priv notify_chan: Option<Chan<UnwindResult>>,
name: Option<SendStr>,
sched: SchedOpts,
stack_size: Option<uint>
@ -273,7 +303,7 @@ impl TaskBuilder {
///
/// # Failure
/// Fails if a future_result was already set for this task.
pub fn future_result(&mut self) -> Port<TaskResult> {
pub fn future_result(&mut self) -> TaskResultPort {
// 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
@ -284,12 +314,12 @@ impl TaskBuilder {
}
// Construct the future and give it to the caller.
let (notify_pipe_po, notify_pipe_ch) = stream::<TaskResult>();
let (notify_pipe_po, notify_pipe_ch) = stream::<UnwindResult>();
// Reconfigure self to use a notify channel.
self.opts.notify_chan = Some(notify_pipe_ch);
notify_pipe_po
TaskResultPort { port: notify_pipe_po }
}
/// Name the task-to-be. Currently the name is used for identification
@ -394,7 +424,7 @@ impl TaskBuilder {
* # Failure
* Fails if a future_result was already set for this task.
*/
pub fn try<T:Send>(&mut self, f: ~fn() -> T) -> Result<T,()> {
pub fn try<T:Send>(&mut self, f: ~fn() -> T) -> Result<T, ~Any> {
let (po, ch) = stream::<T>();
let result = self.future_result();
@ -404,8 +434,8 @@ impl TaskBuilder {
}
match result.recv() {
Success => result::Ok(po.recv()),
Failure => result::Err(())
Ok(()) => Ok(po.recv()),
Err(cause) => Err(cause)
}
}
}
@ -512,7 +542,7 @@ pub fn spawn_sched(mode: SchedMode, f: ~fn()) {
task.spawn(f)
}
pub fn try<T:Send>(f: ~fn() -> T) -> Result<T,()> {
pub fn try<T:Send>(f: ~fn() -> T) -> Result<T, ~Any> {
/*!
* Execute a function in another task and return either the return value
* of the function or result::err.
@ -769,7 +799,7 @@ fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails
fn test_spawn_unlinked_sup_fail_down() {
use rt::test::run_in_uv_task;
do run_in_uv_task {
let result: Result<(),()> = do try {
let result: Result<(), ~Any> = do try {
do spawn_supervised { block_forever(); }
fail!(); // Shouldn't leave a child hanging around.
};
@ -782,7 +812,7 @@ fn test_spawn_unlinked_sup_fail_down() {
fn test_spawn_linked_sup_fail_up() { // child fails; parent fails
use rt::test::run_in_uv_task;
do run_in_uv_task {
let result: Result<(),()> = do try {
let result: Result<(), ~Any> = do try {
// Unidirectional "parenting" shouldn't override bidirectional linked.
// We have to cheat with opts - the interface doesn't support them because
// they don't make sense (redundant with task().supervised()).
@ -803,7 +833,7 @@ fn test_spawn_linked_sup_fail_up() { // child fails; parent fails
fn test_spawn_linked_sup_fail_down() { // parent fails; child fails
use rt::test::run_in_uv_task;
do run_in_uv_task {
let result: Result<(),()> = do try {
let result: Result<(), ~Any> = do try {
// We have to cheat with opts - the interface doesn't support them because
// they don't make sense (redundant with task().supervised()).
let mut b0 = task();
@ -820,7 +850,7 @@ fn test_spawn_linked_sup_fail_down() { // parent fails; child fails
fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails
use rt::test::run_in_uv_task;
do run_in_uv_task {
let result: Result<(),()> = do try {
let result: Result<(), ~Any> = do try {
// Default options are to spawn linked & unsupervised.
do spawn { fail!(); }
block_forever(); // We should get punted awake
@ -833,7 +863,7 @@ fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails
fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails
use rt::test::run_in_uv_task;
do run_in_uv_task {
let result: Result<(),()> = do try {
let result: Result<(), ~Any> = do try {
// Default options are to spawn linked & unsupervised.
do spawn { block_forever(); }
fail!();
@ -846,7 +876,7 @@ fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails
fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails
use rt::test::run_in_uv_task;
do run_in_uv_task {
let result: Result<(),()> = do try {
let result: Result<(), ~Any> = do try {
// Make sure the above test is the same as this one.
let mut builder = task();
builder.linked();
@ -865,7 +895,7 @@ fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails
fn test_spawn_failure_propagate_grandchild() {
use rt::test::run_in_uv_task;
do run_in_uv_task {
let result: Result<(),()> = do try {
let result: Result<(), ~Any> = do try {
// Middle task exits; does grandparent's failure propagate across the gap?
do spawn_supervised {
do spawn_supervised { block_forever(); }
@ -882,7 +912,7 @@ fn test_spawn_failure_propagate_grandchild() {
fn test_spawn_failure_propagate_secondborn() {
use rt::test::run_in_uv_task;
do run_in_uv_task {
let result: Result<(),()> = do try {
let result: Result<(), ~Any> = do try {
// First-born child exits; does parent's failure propagate to sibling?
do spawn_supervised {
do spawn { block_forever(); } // linked
@ -899,7 +929,7 @@ fn test_spawn_failure_propagate_secondborn() {
fn test_spawn_failure_propagate_nephew_or_niece() {
use rt::test::run_in_uv_task;
do run_in_uv_task {
let result: Result<(),()> = do try {
let result: Result<(), ~Any> = do try {
// Our sibling exits; does our failure propagate to sibling's child?
do spawn { // linked
do spawn_supervised { block_forever(); }
@ -916,7 +946,7 @@ fn test_spawn_failure_propagate_nephew_or_niece() {
fn test_spawn_linked_sup_propagate_sibling() {
use rt::test::run_in_uv_task;
do run_in_uv_task {
let result: Result<(),()> = do try {
let result: Result<(), ~Any> = do try {
// Middle sibling exits - does eldest's failure propagate to youngest?
do spawn { // linked
do spawn { block_forever(); } // linked
@ -1024,7 +1054,7 @@ fn test_future_result() {
let mut builder = task();
let result = builder.future_result();
do builder.spawn {}
assert_eq!(result.recv(), Success);
assert!(result.recv().is_ok());
let mut builder = task();
let result = builder.future_result();
@ -1032,7 +1062,7 @@ fn test_future_result() {
do builder.spawn {
fail!();
}
assert_eq!(result.recv(), Failure);
assert!(result.recv().is_err());
}
#[test] #[should_fail]
@ -1057,7 +1087,7 @@ fn test_try_fail() {
match do try {
fail!()
} {
result::Err(()) => (),
result::Err(_) => (),
result::Ok(()) => fail!()
}
}
@ -1393,3 +1423,58 @@ fn test_indestructible() {
assert!(result.is_ok());
}
}
#[test]
fn test_try_fail_cause_static_str() {
match do try {
fail!("static string");
} {
Err(ref e) if e.is::<SendStr>() => {}
Err(_) | Ok(()) => fail!()
}
}
#[test]
fn test_try_fail_cause_owned_str() {
match do try {
fail!(~"owned string");
} {
Err(ref e) if e.is::<SendStr>() => {}
Err(_) | Ok(()) => fail!()
}
}
#[test]
fn test_try_fail_cause_any() {
match do try {
fail!(~413u16 as ~Any);
} {
Err(ref e) if e.is::<u16>() => {}
Err(_) | Ok(()) => fail!()
}
}
#[ignore(reason = "linked failure")]
#[test]
fn test_try_fail_cause_linked() {
match do try {
do spawn {
fail!()
}
} {
Err(ref e) if e.is::<LinkedFailure>() => {}
Err(_) | Ok(()) => fail!()
}
}
#[test]
fn test_try_fail_cause_any_wrapped() {
struct Juju;
match do try {
fail!(~Juju)
} {
Err(ref e) if e.is::<Juju>() => {}
Err(_) | Ok(()) => fail!()
}
}

View File

@ -76,21 +76,24 @@ use prelude::*;
use cast::transmute;
use cast;
use cell::Cell;
use container::MutableMap;
use comm::{Chan, GenericChan, oneshot};
use container::MutableMap;
use hashmap::{HashSet, HashSetMoveIterator};
use local_data;
use task::{Failure, SingleThreaded};
use task::{Success, TaskOpts, TaskResult};
use task::unkillable;
use uint;
use util;
use unstable::sync::Exclusive;
use rt::in_green_task_context;
use rt::local::Local;
use rt::task::{Task, Sched};
use rt::shouldnt_be_public::{Scheduler, KillHandle, WorkQueue, Thread, EventLoop};
use rt::task::{Task, Sched};
use rt::task::{UnwindReasonLinked, UnwindReasonStr};
use rt::task::{UnwindResult, Success, Failure};
use rt::uv::uvio::UvEventLoop;
use send_str::IntoSendStr;
use task::SingleThreaded;
use task::TaskOpts;
use task::unkillable;
use uint;
use unstable::sync::Exclusive;
use util;
#[cfg(test)] use task::default_task_opts;
#[cfg(test)] use comm;
@ -321,7 +324,7 @@ impl Drop for Taskgroup {
do RuntimeGlue::with_task_handle_and_failing |me, failing| {
if failing {
for x in self.notifier.mut_iter() {
x.failed = true;
x.task_result = Some(Failure(UnwindReasonLinked));
}
// Take everybody down with us. After this point, every
// other task in the group will see 'tg' as none, which
@ -353,7 +356,7 @@ pub fn Taskgroup(tasks: TaskGroupArc,
ancestors: AncestorList,
mut notifier: Option<AutoNotify>) -> Taskgroup {
for x in notifier.mut_iter() {
x.failed = false;
x.task_result = Some(Success);
}
Taskgroup {
@ -364,21 +367,28 @@ pub fn Taskgroup(tasks: TaskGroupArc,
}
struct AutoNotify {
notify_chan: Chan<TaskResult>,
failed: bool,
notify_chan: Chan<UnwindResult>,
// XXX: By value self drop would allow this to be a plain UnwindResult
task_result: Option<UnwindResult>,
}
impl AutoNotify {
pub fn new(chan: Chan<UnwindResult>) -> AutoNotify {
AutoNotify {
notify_chan: chan,
// Un-set above when taskgroup successfully made.
task_result: Some(Failure(UnwindReasonStr("AutoNotify::new()".into_send_str())))
}
}
}
impl Drop for AutoNotify {
fn drop(&mut self) {
let result = if self.failed { Failure } else { Success };
self.notify_chan.send(result);
}
}
let result = self.task_result.take_unwrap();
fn AutoNotify(chan: Chan<TaskResult>) -> AutoNotify {
AutoNotify {
notify_chan: chan,
failed: true // Un-set above when taskgroup successfully made.
self.notify_chan.send(result);
}
}
@ -675,10 +685,8 @@ pub fn spawn_raw(mut opts: TaskOpts, f: ~fn()) {
if opts.notify_chan.is_some() {
let notify_chan = opts.notify_chan.take_unwrap();
let notify_chan = Cell::new(notify_chan);
let on_exit: ~fn(bool) = |success| {
notify_chan.take().send(
if success { Success } else { Failure }
)
let on_exit: ~fn(UnwindResult) = |task_result| {
notify_chan.take().send(task_result)
};
task.death.on_exit = Some(on_exit);
}
@ -721,7 +729,7 @@ fn test_spawn_raw_notify_success() {
};
do spawn_raw(opts) {
}
assert_eq!(notify_po.recv(), Success);
assert!(notify_po.recv().is_success());
}
#[test]
@ -738,5 +746,5 @@ fn test_spawn_raw_notify_failure() {
do spawn_raw(opts) {
fail!();
}
assert_eq!(notify_po.recv(), Failure);
assert!(notify_po.recv().is_failure());
}

View File

@ -14,7 +14,6 @@ use comm;
use libc;
use ptr;
use option::*;
use either::{Either, Left, Right};
use task;
use unstable::atomics::{AtomicOption,AtomicUint,Acquire,Release,Relaxed,SeqCst};
use unstable::finally::Finally;
@ -31,6 +30,27 @@ pub struct UnsafeArc<T> {
data: *mut ArcData<T>,
}
pub enum UnsafeArcUnwrap<T> {
UnsafeArcSelf(UnsafeArc<T>),
UnsafeArcT(T)
}
impl<T> UnsafeArcUnwrap<T> {
fn expect_t(self, msg: &'static str) -> T {
match self {
UnsafeArcSelf(_) => fail!(msg),
UnsafeArcT(t) => t
}
}
fn is_self(&self) -> bool {
match *self {
UnsafeArcSelf(_) => true,
UnsafeArcT(_) => false
}
}
}
struct ArcData<T> {
count: AtomicUint,
// An unwrapper uses this protocol to communicate with the "other" task that
@ -178,9 +198,9 @@ impl<T: Send> UnsafeArc<T> {
}
}
/// As unwrap above, but without blocking. Returns 'Left(self)' if this is
/// not the last reference; 'Right(unwrapped_data)' if so.
pub fn try_unwrap(self) -> Either<UnsafeArc<T>, T> {
/// As unwrap above, but without blocking. Returns 'UnsafeArcSelf(self)' if this is
/// not the last reference; 'UnsafeArcT(unwrapped_data)' if so.
pub fn try_unwrap(self) -> UnsafeArcUnwrap<T> {
unsafe {
let mut this = self; // FIXME(#4330) mutable self
// The ~ dtor needs to run if this code succeeds.
@ -198,10 +218,10 @@ impl<T: Send> UnsafeArc<T> {
// Tell this handle's destructor not to run (we are now it).
this.data = ptr::mut_null();
// FIXME(#3224) as above
Right(data.data.take_unwrap())
UnsafeArcT(data.data.take_unwrap())
} else {
cast::forget(data);
Left(this)
UnsafeArcSelf(this)
}
}
}
@ -574,7 +594,7 @@ mod tests {
#[test]
fn arclike_try_unwrap() {
let x = UnsafeArc::new(~~"hello");
assert!(x.try_unwrap().expect_right("try_unwrap failed") == ~~"hello");
assert!(x.try_unwrap().expect_t("try_unwrap failed") == ~~"hello");
}
#[test]
@ -582,9 +602,9 @@ mod tests {
let x = UnsafeArc::new(~~"hello");
let x2 = x.clone();
let left_x = x.try_unwrap();
assert!(left_x.is_left());
assert!(left_x.is_self());
util::ignore(left_x);
assert!(x2.try_unwrap().expect_right("try_unwrap none") == ~~"hello");
assert!(x2.try_unwrap().expect_t("try_unwrap none") == ~~"hello");
}
#[test]
@ -601,7 +621,7 @@ mod tests {
p.recv();
task::deschedule(); // Try to make the unwrapper get blocked first.
let left_x = x.try_unwrap();
assert!(left_x.is_left());
assert!(left_x.is_self());
util::ignore(left_x);
p.recv();
}
@ -649,7 +669,7 @@ mod tests {
assert!(x2.unwrap() == ~~"hello");
}
assert!(x.unwrap() == ~~"hello");
assert!(res.recv() == task::Success);
assert!(res.recv().is_ok());
}
#[test]

View File

@ -60,7 +60,7 @@ fn spawn_supervised_blocking(myname: &str, f: ~fn()) {
builder.spawn(f);
error!("{} group waiting", myname);
let x = res.recv();
assert_eq!(x, task::Success);
assert!(x.is_ok());
}
fn main() {
@ -78,7 +78,7 @@ fn main() {
// Main group #0 waits for unsupervised group #1.
// Grandparent group #1 waits for middle group #2, then fails, killing #3.
// Middle group #2 creates grandchild_group #3, waits for it to be ready, exits.
let x: result::Result<(),()> = do task::try { // unlinked
let x: result::Result<(), ~Any> = do task::try { // unlinked
do spawn_supervised_blocking("grandparent") {
do spawn_supervised_blocking("middle") {
grandchild_group(num_tasks);

View File

@ -0,0 +1,15 @@
// Copyright 2013 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.
// error-pattern:failed at '~Any'
fn main() {
fail!(~612_i64);
}

View File

@ -0,0 +1,15 @@
// Copyright 2013 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.
// error-pattern:failed at '~Any'
fn main() {
fail!(~413 as ~Any);
}

View File

@ -0,0 +1,15 @@
// Copyright 2013 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.
// error-pattern:failed at 'test-fail-send-str'
fn main() {
fail!("test-fail-send-str".into_send_str());
}

View File

@ -24,5 +24,7 @@ pub fn main() {
let x = do task::try {
let _b = Foo;
};
assert_eq!(x, Err(()));
let s = x.unwrap_err().move::<SendStr>().unwrap();
assert_eq!(s.as_slice(), "This failure should happen.");
}