about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorMarvin Löbel <loebel.marvin@gmail.com>2013-10-11 23:20:34 +0200
committerMarvin Löbel <loebel.marvin@gmail.com>2013-10-28 08:50:32 +0100
commitfa8e71a8257f4226ab532d4bf268d3ecbfa98eb4 (patch)
tree0b8051814dd8a5ef08e663c172e2b456065d625d /src/libstd
parentcb5b21eba713ff3888b2741db4c9e7d841cfde02 (diff)
downloadrust-fa8e71a8257f4226ab532d4bf268d3ecbfa98eb4.tar.gz
rust-fa8e71a8257f4226ab532d4bf268d3ecbfa98eb4.zip
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>`.
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/any.rs418
-rw-r--r--src/libstd/prelude.rs2
-rw-r--r--src/libstd/rt/io/option.rs2
-rw-r--r--src/libstd/rt/io/signal.rs5
-rw-r--r--src/libstd/rt/kill.rs34
-rw-r--r--src/libstd/rt/mod.rs7
-rw-r--r--src/libstd/rt/sched.rs4
-rw-r--r--src/libstd/rt/task.rs174
-rw-r--r--src/libstd/rt/test.rs47
-rw-r--r--src/libstd/rt/uv/uvio.rs19
-rw-r--r--src/libstd/run.rs2
-rw-r--r--src/libstd/std.rs14
-rw-r--r--src/libstd/sys.rs62
-rw-r--r--src/libstd/task/mod.rs165
-rw-r--r--src/libstd/task/spawn.rs60
-rw-r--r--src/libstd/unstable/sync.rs42
16 files changed, 855 insertions, 202 deletions
diff --git a/src/libstd/any.rs b/src/libstd/any.rs
new file mode 100644
index 00000000000..a648c5e6878
--- /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 <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>);
+    }
+}
diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs
index bb8e6674b46..a15ef879e32 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 52699964b62..234b46458b4 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 d2266c8d5d6..4c6c675df03 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 19f17ca966d..edf6ffb820b 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<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.
diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs
index d87580c83bf..eaaf8c43281 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 9965380d9dc..d44264befc1 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 f82eb929a39..8f695763a25 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<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?")
-        };
+        // 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("<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);
+            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);
     }
 }
 
diff --git a/src/libstd/rt/test.rs b/src/libstd/rt/test.rs
index e4bbfe0a5a3..5f78b9fc959 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 e0707a86f7b..5643f6445f1 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 615ba60e066..6e7d681d40a 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 12316cb5ead..069a390f010 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 d20a6696e27..b35b25aeb6f 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<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!()) }
+
+    #[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 b72d6773ec5..3333b24a924 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<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!()
+    }
+}
diff --git a/src/libstd/task/spawn.rs b/src/libstd/task/spawn.rs
index fbe2988f77c..235e67048f6 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<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 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<UnwindResult>) -> 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<TaskResult>) -> 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 2b036c318ba..28cebfb6146 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<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]