about summary refs log tree commit diff
path: root/src/libstd/task
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/task
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/task')
-rw-r--r--src/libstd/task/mod.rs165
-rw-r--r--src/libstd/task/spawn.rs60
2 files changed, 159 insertions, 66 deletions
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());
 }