diff --git a/src/libextra/sync.rs b/src/libextra/sync.rs index 131cf063d1d35..1988da2b0bafc 100644 --- a/src/libextra/sync.rs +++ b/src/libextra/sync.rs @@ -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!(); } diff --git a/src/libextra/test.rs b/src/libextra/test.rs index 457f7868e237a..070108ddf2e8e 100644 --- a/src/libextra/test.rs +++ b/src/libextra/test.rs @@ -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)); } } diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index ce5e81d410923..f5973bff4fccf 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -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] diff --git a/src/libstd/any.rs b/src/libstd/any.rs new file mode 100644 index 0000000000000..a648c5e68783b --- /dev/null +++ b/src/libstd/any.rs @@ -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 or the MIT license +// , 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() -> TypeId { + TypeId{ t: unsafe { get_tydesc::() } } + } +} + +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::() + } + + /// 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 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(self) -> bool; + + /// Returns some reference to the boxed value if it is of type `T`, or + /// `None` if it isn't. + fn as_ref(self) -> Option<&'self T>; +} + +impl<'self> AnyRefExt<'self> for &'self Any { + #[inline] + fn is(self) -> bool { + // Get TypeId of the type this function is instantiated with + let t = TypeId::of::(); + + // 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(self) -> Option<&'self T> { + if self.is::() { + 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(self) -> Option<&'self mut T>; +} + +impl<'self> AnyMutRefExt<'self> for &'self mut Any { + #[inline] + fn as_mut(self) -> Option<&'self mut T> { + if self.is::() { + 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(self) -> Option<~T>; +} + +impl AnyOwnExt for ~Any { + #[inline] + fn move(self) -> Option<~T> { + if self.is::() { + 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::(), TypeId::of::<&str>(), TypeId::of::()); + let (d, e, f) = (TypeId::of::(), TypeId::of::<&str>(), TypeId::of::()); + + 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::()); + assert!(!b.is::()); + assert!(!c.is::()); + + assert!(!a.is::<&'static str>()); + assert!(b.is::<&'static str>()); + assert!(!c.is::<&'static str>()); + + assert!(!a.is::()); + assert!(!b.is::()); + assert!(c.is::()); + } + + #[test] + fn any_owning() { + let (a, b, c) = (~5u as ~Any, ~TEST as ~Any, ~Test as ~Any); + + assert!(a.is::()); + assert!(!b.is::()); + assert!(!c.is::()); + + assert!(!a.is::<&'static str>()); + assert!(b.is::<&'static str>()); + assert!(!c.is::<&'static str>()); + + assert!(!a.is::()); + assert!(!b.is::()); + assert!(c.is::()); + } + + #[test] + fn any_managed() { + let (a, b, c) = (@5u as @Any, @TEST as @Any, @Test as @Any); + + assert!(a.is::()); + assert!(!b.is::()); + assert!(!c.is::()); + + assert!(!a.is::<&'static str>()); + assert!(b.is::<&'static str>()); + assert!(!c.is::<&'static str>()); + + assert!(!a.is::()); + assert!(!b.is::()); + assert!(c.is::()); + } + + #[test] + fn any_as_ref() { + let a = &5u as &Any; + + match a.as_ref::() { + Some(&5) => {} + x => fail!("Unexpected value {:?}", x) + } + + match a.as_ref::() { + 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::() { + Some(x) => { + assert_eq!(*x, 5u); + *x = 612; + } + x => fail!("Unexpected value {:?}", x) + } + + match b_r.as_mut::() { + Some(x) => { + assert_eq!(*x, 7u); + *x = 413; + } + x => fail!("Unexpected value {:?}", x) + } + + match a_r.as_mut::() { + None => (), + x => fail!("Unexpected value {:?}", x) + } + + match b_r.as_mut::() { + None => (), + x => fail!("Unexpected value {:?}", x) + } + + match a_r.as_mut::() { + Some(&612) => {} + x => fail!("Unexpected value {:?}", x) + } + + match b_r.as_mut::() { + 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>); + } +} diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index bb8e6674b464b..a15ef879e322d 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -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; diff --git a/src/libstd/rt/io/option.rs b/src/libstd/rt/io/option.rs index 52699964b6241..234b46458b458 100644 --- a/src/libstd/rt/io/option.rs +++ b/src/libstd/rt/io/option.rs @@ -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() { diff --git a/src/libstd/rt/io/signal.rs b/src/libstd/rt/io/signal.rs index d2266c8d5d671..4c6c675df0342 100644 --- a/src/libstd/rt/io/signal.rs +++ b/src/libstd/rt/io/signal.rs @@ -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(|_| { diff --git a/src/libstd/rt/kill.rs b/src/libstd/rt/kill.rs index 19f17ca966d2d..edf6ffb820b31 100644 --- a/src/libstd/rt/kill.rs +++ b/src/libstd/rt/kill.rs @@ -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, // 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) { + pub fn collect_failure(&mut self, result: UnwindResult, group: Option) { // 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. diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index d87580c83bfdb..eaaf8c4328191 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -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 diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs index 9965380d9dc31..d44264befc16f 100644 --- a/src/libstd/rt/sched.rs +++ b/src/libstd/rt/sched.rs @@ -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); diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index f82eb929a3959..8f695763a255f 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -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); +/// 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 +} + +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?") - }; + // 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... - 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) + 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(); } - 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(""); - - // 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); + task = Local::unsafe_borrow(); + let n = (*task).name.as_ref().map(|n| n.as_slice()).unwrap_or(""); + + // 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); } } diff --git a/src/libstd/rt/test.rs b/src/libstd/rt/test.rs index e4bbfe0a5a3e9..5f78b9fc9591e 100644 --- a/src/libstd/rt/test.rs +++ b/src/libstd/rt/test.rs @@ -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(()) } } diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs index e0707a86f7bbc..5643f6445f16f 100644 --- a/src/libstd/rt/uv/uvio.rs +++ b/src/libstd/rt/uv/uvio.rs @@ -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() = || { diff --git a/src/libstd/run.rs b/src/libstd/run.rs index 615ba60e066c0..6e7d681d40a05 100644 --- a/src/libstd/run.rs +++ b/src/libstd/run.rs @@ -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); diff --git a/src/libstd/std.rs b/src/libstd/std.rs index 12316cb5ead2a..069a390f0103e 100644 --- a/src/libstd/std.rs +++ b/src/libstd/std.rs @@ -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 logging; + pub use option; pub use os; - pub use fmt; + pub use str; + pub use sys; pub use to_bytes; - pub use logging; + pub use to_str; + pub use unstable; } diff --git a/src/libstd/sys.rs b/src/libstd/sys.rs index d20a6696e278f..b35b25aeb6f72 100644 --- a/src/libstd/sys.rs +++ b/src/libstd/sys.rs @@ -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 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!()) } + + #[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_owned() { FailWithCause::fail_with(~"cause", file!(), line!()) } + fn fail_any_wrap() { FailWithCause::fail_with(~413_u16, file!(), line!()) } } diff --git a/src/libstd/task/mod.rs b/src/libstd/task/mod.rs index b72d6773ec567..3333b24a924d2 100644 --- a/src/libstd/task/mod.rs +++ b/src/libstd/task/mod.rs @@ -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 +} + +impl GenericPort for TaskResultPort { + #[inline] + fn recv(&self) -> TaskResult { + wrap_as_any(self.port.recv()) + } + + #[inline] + fn try_recv(&self) -> Option { + self.port.try_recv().map(wrap_as_any) + } +} + +impl Peekable 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>, + priv notify_chan: Option>, name: Option, sched: SchedOpts, stack_size: Option @@ -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 { + 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::(); + let (notify_pipe_po, notify_pipe_ch) = stream::(); // 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(&mut self, f: ~fn() -> T) -> Result { + pub fn try(&mut self, f: ~fn() -> T) -> Result { let (po, ch) = stream::(); 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(f: ~fn() -> T) -> Result { +pub fn try(f: ~fn() -> T) -> Result { /*! * 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::() => {} + Err(_) | Ok(()) => fail!() + } +} + +#[test] +fn test_try_fail_cause_owned_str() { + match do try { + fail!(~"owned string"); + } { + Err(ref e) if e.is::() => {} + Err(_) | Ok(()) => fail!() + } +} + +#[test] +fn test_try_fail_cause_any() { + match do try { + fail!(~413u16 as ~Any); + } { + Err(ref e) if e.is::() => {} + 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::() => {} + Err(_) | Ok(()) => fail!() + } +} + +#[test] +fn test_try_fail_cause_any_wrapped() { + struct Juju; + + match do try { + fail!(~Juju) + } { + Err(ref e) if e.is::() => {} + Err(_) | Ok(()) => fail!() + } +} diff --git a/src/libstd/task/spawn.rs b/src/libstd/task/spawn.rs index fbe2988f77c71..235e67048f678 100644 --- a/src/libstd/task/spawn.rs +++ b/src/libstd/task/spawn.rs @@ -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) -> 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, - failed: bool, + notify_chan: Chan, + + // XXX: By value self drop would allow this to be a plain UnwindResult + task_result: Option, } -impl Drop for AutoNotify { - fn drop(&mut self) { - let result = if self.failed { Failure } else { Success }; - self.notify_chan.send(result); +impl AutoNotify { + pub fn new(chan: Chan) -> AutoNotify { + AutoNotify { + notify_chan: chan, + + // Un-set above when taskgroup successfully made. + task_result: Some(Failure(UnwindReasonStr("AutoNotify::new()".into_send_str()))) + } } } -fn AutoNotify(chan: Chan) -> AutoNotify { - AutoNotify { - notify_chan: chan, - failed: true // Un-set above when taskgroup successfully made. +impl Drop for AutoNotify { + fn drop(&mut self) { + let result = self.task_result.take_unwrap(); + + 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()); } diff --git a/src/libstd/unstable/sync.rs b/src/libstd/unstable/sync.rs index 2b036c318bac7..28cebfb614600 100644 --- a/src/libstd/unstable/sync.rs +++ b/src/libstd/unstable/sync.rs @@ -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 { data: *mut ArcData, } +pub enum UnsafeArcUnwrap { + UnsafeArcSelf(UnsafeArc), + UnsafeArcT(T) +} + +impl UnsafeArcUnwrap { + 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 { count: AtomicUint, // An unwrapper uses this protocol to communicate with the "other" task that @@ -178,9 +198,9 @@ impl UnsafeArc { } } - /// 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, 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 { unsafe { let mut this = self; // FIXME(#4330) mutable self // The ~ dtor needs to run if this code succeeds. @@ -198,10 +218,10 @@ impl UnsafeArc { // 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] diff --git a/src/test/bench/task-perf-linked-failure.rs b/src/test/bench/task-perf-linked-failure.rs index 5484a3965b3a8..54a05c7257e83 100644 --- a/src/test/bench/task-perf-linked-failure.rs +++ b/src/test/bench/task-perf-linked-failure.rs @@ -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); diff --git a/src/test/run-fail/fail-macro-any-wrapped.rs b/src/test/run-fail/fail-macro-any-wrapped.rs new file mode 100644 index 0000000000000..5a3dda88f705a --- /dev/null +++ b/src/test/run-fail/fail-macro-any-wrapped.rs @@ -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 or the MIT license +// , 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); +} diff --git a/src/test/run-fail/fail-macro-any.rs b/src/test/run-fail/fail-macro-any.rs new file mode 100644 index 0000000000000..58f3522ce7a97 --- /dev/null +++ b/src/test/run-fail/fail-macro-any.rs @@ -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 or the MIT license +// , 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); +} diff --git a/src/test/run-fail/fail-macro-send_str.rs b/src/test/run-fail/fail-macro-send_str.rs new file mode 100644 index 0000000000000..2047fde150b9a --- /dev/null +++ b/src/test/run-fail/fail-macro-send_str.rs @@ -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 or the MIT license +// , 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()); +} diff --git a/src/test/run-pass/unit-like-struct-drop-run.rs b/src/test/run-pass/unit-like-struct-drop-run.rs index 7b450168cc4a8..8d4680436a423 100644 --- a/src/test/run-pass/unit-like-struct-drop-run.rs +++ b/src/test/run-pass/unit-like-struct-drop-run.rs @@ -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::().unwrap(); + assert_eq!(s.as_slice(), "This failure should happen."); }