about summary refs log tree commit diff
path: root/src/libstd/rt/task.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/rt/task.rs')
-rw-r--r--src/libstd/rt/task.rs174
1 files changed, 124 insertions, 50 deletions
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);
     }
 }