about summary refs log tree commit diff
path: root/src/libstd/task
diff options
context:
space:
mode:
authorBen Blum <bblum@andrew.cmu.edu>2013-07-16 17:28:46 -0400
committerBen Blum <bblum@andrew.cmu.edu>2013-07-20 05:12:04 -0400
commit7ad7911222f0395dd2babaf12410fb15c9aa938f (patch)
treea983cd583eb539a5d0781915be39032d74aeabc4 /src/libstd/task
parent21831458500b1a5c78fe6aeccab5412fac701d9f (diff)
downloadrust-7ad7911222f0395dd2babaf12410fb15c9aa938f.tar.gz
rust-7ad7911222f0395dd2babaf12410fb15c9aa938f.zip
Add watched and indestructible spawn modes.
Diffstat (limited to 'src/libstd/task')
-rw-r--r--src/libstd/task/mod.rs110
-rw-r--r--src/libstd/task/spawn.rs13
2 files changed, 120 insertions, 3 deletions
diff --git a/src/libstd/task/mod.rs b/src/libstd/task/mod.rs
index 04ce2472448..a3ece0c2e0a 100644
--- a/src/libstd/task/mod.rs
+++ b/src/libstd/task/mod.rs
@@ -148,6 +148,17 @@ pub struct SchedOpts {
  * * supervised - Propagate failure unidirectionally from parent to child,
  *                but not from child to parent. False by default.
  *
+ * * watched - Make parent task collect exit status notifications from child
+ *             before reporting its own exit status. (This delays the parent
+ *             task's death and cleanup until after all transitively watched
+ *             children also exit.) True by default.
+ *
+ * * indestructible - Configures the task to ignore kill signals received from
+ *                    linked failure. This may cause process hangs during
+ *                    failure if not used carefully, but causes task blocking
+ *                    code paths (e.g. port recv() calls) to be faster by 2
+ *                    atomic operations. False by default.
+ *
  * * notify_chan - Enable lifecycle notifications on the given channel
  *
  * * sched - Specify the configuration of a new scheduler to create the task
@@ -166,6 +177,8 @@ pub struct SchedOpts {
 pub struct TaskOpts {
     linked: bool,
     supervised: bool,
+    watched: bool,
+    indestructible: bool,
     notify_chan: Option<Chan<TaskResult>>,
     sched: SchedOpts
 }
@@ -217,6 +230,8 @@ impl TaskBuilder {
             opts: TaskOpts {
                 linked: self.opts.linked,
                 supervised: self.opts.supervised,
+                watched: self.opts.watched,
+                indestructible: self.opts.indestructible,
                 notify_chan: notify_chan,
                 sched: self.opts.sched
             },
@@ -232,6 +247,7 @@ impl TaskBuilder {
     /// the other will not be killed.
     pub fn unlinked(&mut self) {
         self.opts.linked = false;
+        self.opts.watched = false;
     }
 
     /// Unidirectionally link the child task's failure with the parent's. The
@@ -240,6 +256,7 @@ impl TaskBuilder {
     pub fn supervised(&mut self) {
         self.opts.supervised = true;
         self.opts.linked = false;
+        self.opts.watched = false;
     }
 
     /// Link the child task's and parent task's failures. If either fails, the
@@ -247,6 +264,26 @@ impl TaskBuilder {
     pub fn linked(&mut self) {
         self.opts.linked = true;
         self.opts.supervised = false;
+        self.opts.watched = true;
+    }
+
+    /// Cause the parent task to collect the child's exit status (and that of
+    /// all transitively-watched grandchildren) before reporting its own.
+    pub fn watched(&mut self) {
+        self.opts.watched = true;
+    }
+
+    /// Allow the child task to outlive the parent task, at the possible cost
+    /// of the parent reporting success even if the child task fails later.
+    pub fn unwatched(&mut self) {
+        self.opts.watched = false;
+    }
+
+    /// Cause the child task to ignore any kill signals received from linked
+    /// failure. This optimizes context switching, at the possible expense of
+    /// process hangs in the case of unexpected failure.
+    pub fn indestructible(&mut self) {
+        self.opts.indestructible = true;
     }
 
     /**
@@ -341,6 +378,8 @@ impl TaskBuilder {
         let opts = TaskOpts {
             linked: x.opts.linked,
             supervised: x.opts.supervised,
+            watched: x.opts.watched,
+            indestructible: x.opts.indestructible,
             notify_chan: notify_chan,
             sched: x.opts.sched
         };
@@ -407,6 +446,8 @@ pub fn default_task_opts() -> TaskOpts {
     TaskOpts {
         linked: true,
         supervised: false,
+        watched: true,
+        indestructible: false,
         notify_chan: None,
         sched: SchedOpts {
             mode: DefaultScheduler,
@@ -448,6 +489,17 @@ pub fn spawn_supervised(f: ~fn()) {
     task.spawn(f)
 }
 
+/// Creates a child task that cannot be killed by linked failure. This causes
+/// its context-switch path to be faster by 2 atomic swap operations.
+/// (Note that this convenience wrapper still uses linked-failure, so the
+/// child's children will still be killable by the parent. For the fastest
+/// possible spawn mode, use task::task().unlinked().indestructible().spawn.)
+pub fn spawn_indestructible(f: ~fn()) {
+    let mut task = task();
+    task.indestructible();
+    task.spawn(f)
+}
+
 pub fn spawn_with<A:Send>(arg: A, f: ~fn(v: A)) {
     /*!
      * Runs a task, while transfering ownership of one argument to the
@@ -1209,3 +1261,61 @@ fn test_simple_newsched_spawn() {
     }
 }
 
+#[test] #[ignore(cfg(windows))]
+fn test_spawn_watched() {
+    use rt::test::{run_in_newsched_task, spawntask_try};
+    do run_in_newsched_task {
+        let result = do spawntask_try {
+            let mut t = task();
+            t.unlinked();
+            t.watched();
+            do t.spawn {
+                let mut t = task();
+                t.unlinked();
+                t.watched();
+                do t.spawn {
+                    task::yield();
+                    fail!();
+                }
+            }
+        };
+        assert!(result.is_err());
+    }
+}
+
+#[test] #[ignore(cfg(windows))]
+fn test_indestructible() {
+    use rt::test::{run_in_newsched_task, spawntask_try};
+    do run_in_newsched_task {
+        let result = do spawntask_try {
+            let mut t = task();
+            t.watched();
+            t.supervised();
+            t.indestructible();
+            do t.spawn {
+                let (p1, _c1) = stream::<()>();
+                let (p2, c2) = stream::<()>();
+                let (p3, c3) = stream::<()>();
+                let mut t = task();
+                t.unwatched();
+                do t.spawn {
+                    do (|| {
+                        p1.recv(); // would deadlock if not killed
+                    }).finally {
+                        c2.send(());
+                    };
+                }
+                let mut t = task();
+                t.unwatched();
+                do t.spawn {
+                    p3.recv();
+                    task::yield();
+                    fail!();
+                }
+                c3.send(());
+                p2.recv();
+            }
+        };
+        assert!(result.is_ok());
+    }
+}
diff --git a/src/libstd/task/spawn.rs b/src/libstd/task/spawn.rs
index 9734a41ba3b..2150c0c5ac2 100644
--- a/src/libstd/task/spawn.rs
+++ b/src/libstd/task/spawn.rs
@@ -671,6 +671,7 @@ pub fn spawn_raw(opts: TaskOpts, f: ~fn()) {
 
 fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
     let child_data = Cell::new(gen_child_taskgroup(opts.linked, opts.supervised));
+    let indestructible = opts.indestructible;
 
     let child_wrapper: ~fn() = || {
         // Child task runs this code.
@@ -692,7 +693,11 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
         };
         // Should be run after the local-borrowed task is returned.
         if enlist_success {
-            f()
+            if indestructible {
+                unsafe { do unkillable { f() } }
+            } else {
+                f()
+            }
         }
     };
 
@@ -700,13 +705,13 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
         let sched = Local::unsafe_borrow::<Scheduler>();
         rtdebug!("unsafe borrowed sched");
 
-        if opts.linked {
+        if opts.watched {
             let child_wrapper = Cell::new(child_wrapper);
             do Local::borrow::<Task, ~Task>() |running_task| {
                 ~running_task.new_child(&mut (*sched).stack_pool, child_wrapper.take())
             }
         } else {
-            // An unlinked task is a new root in the task tree
+            // An unwatched task is a new root in the exit-code propagation tree
             ~Task::new_root(&mut (*sched).stack_pool, child_wrapper)
         }
     };
@@ -848,6 +853,7 @@ fn test_spawn_raw_simple() {
 fn test_spawn_raw_unsupervise() {
     let opts = task::TaskOpts {
         linked: false,
+        watched: false,
         notify_chan: None,
         .. default_task_opts()
     };
@@ -878,6 +884,7 @@ fn test_spawn_raw_notify_failure() {
 
     let opts = task::TaskOpts {
         linked: false,
+        watched: false,
         notify_chan: Some(notify_ch),
         .. default_task_opts()
     };