mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-01 15:01:51 +00:00
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:
parent
cb5b21eba7
commit
fa8e71a825
@ -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!();
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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
418
src/libstd/any.rs
Normal 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>);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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(|_| {
|
||||
|
@ -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,14 +495,16 @@ 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,
|
||||
child_tombstones: Some(f), _ }) => {
|
||||
UnsafeArcT(KillHandleInner { any_child_failed: false,
|
||||
child_tombstones: Some(f), _ }) => {
|
||||
let f = Cell::new(f); // :(
|
||||
do add_lazy_tombstone(parent) |other_tombstones| {
|
||||
let f = Cell::new(f.take()); // :(
|
||||
@ -513,9 +516,10 @@ impl KillHandle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All children exited, none failed. Nothing to do!
|
||||
Right(KillHandleInner { any_child_failed: false,
|
||||
child_tombstones: None, _ }) => { }
|
||||
UnsafeArcT(KillHandleInner { any_child_failed: false,
|
||||
child_tombstones: None, _ }) => { }
|
||||
}
|
||||
|
||||
// NB: Takes a pthread mutex -- 'blk' not allowed to reschedule.
|
||||
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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,55 +617,73 @@ 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?")
|
||||
};
|
||||
|
||||
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)
|
||||
}
|
||||
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();
|
||||
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
|
||||
// either be sent to the parent task (ideally), or get printed to
|
||||
// the task's logger. Right now the logger is actually a uvio
|
||||
// instance, which uses unkillable blocks internally for various
|
||||
// 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);
|
||||
let task: *mut Task;
|
||||
|
||||
{
|
||||
let msg = match reason {
|
||||
UnwindReasonStr(ref s) => s.as_slice(),
|
||||
UnwindReasonAny(_) => "~Any",
|
||||
UnwindReasonLinked => "linked failure",
|
||||
};
|
||||
|
||||
if !in_green_task_context() {
|
||||
rterrln!("failed in non-task context at '{}', {}:{}",
|
||||
msg, file, line);
|
||||
intrinsics::abort();
|
||||
}
|
||||
|
||||
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
|
||||
// either be sent to the parent task (ideally), or get printed to
|
||||
// the task's logger. Right now the logger is actually a uvio
|
||||
// instance, which uses unkillable blocks internally for various
|
||||
// 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.
|
||||
|
||||
rterrln!("task '{}' failed at '{}', {}:{}", n, msg, file, line);
|
||||
|
||||
if (*task).unwinder.unwinding {
|
||||
rtabort!("unwinding again");
|
||||
}
|
||||
None => rterrln!("task '{}' failed at '{}'", n, msg),
|
||||
}
|
||||
if (*task).unwinder.unwinding {
|
||||
rtabort!("unwinding again");
|
||||
}
|
||||
(*task).unwinder.begin_unwind();
|
||||
|
||||
(*task).unwinder.begin_unwind(reason);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(()) }
|
||||
|
||||
}
|
||||
|
||||
|
@ -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() = || {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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() {
|
||||
@ -74,9 +88,21 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn fail_static() { FailWithCause::fail_with("cause", file!(), line!()) }
|
||||
fn fail_static() { FailWithCause::fail_with("cause", file!(), line!()) }
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn fail_owned() { FailWithCause::fail_with(~"cause", file!(), line!()) }
|
||||
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!()) }
|
||||
}
|
||||
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
|
15
src/test/run-fail/fail-macro-any-wrapped.rs
Normal file
15
src/test/run-fail/fail-macro-any-wrapped.rs
Normal 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);
|
||||
}
|
15
src/test/run-fail/fail-macro-any.rs
Normal file
15
src/test/run-fail/fail-macro-any.rs
Normal 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);
|
||||
}
|
15
src/test/run-fail/fail-macro-send_str.rs
Normal file
15
src/test/run-fail/fail-macro-send_str.rs
Normal 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());
|
||||
}
|
@ -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.");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user