]> git.lizzy.rs Git - rust.git/commitdiff
Add watched and indestructible spawn modes.
authorBen Blum <bblum@andrew.cmu.edu>
Tue, 16 Jul 2013 21:28:46 +0000 (17:28 -0400)
committerBen Blum <bblum@andrew.cmu.edu>
Sat, 20 Jul 2013 09:12:04 +0000 (05:12 -0400)
src/libstd/rt/kill.rs
src/libstd/task/mod.rs
src/libstd/task/spawn.rs

index b331442eb4bc2c80520e453387f862217d586262..cfd8e46dfdb75f721d3a95f9f2a6ea348345a360 100644 (file)
@@ -130,7 +130,7 @@ pub fn wake(self) -> Option<~Task> {
 
     /// Create a blocked task, unless the task was already killed.
     pub fn try_block(mut task: ~Task) -> Either<~Task, BlockedTask> {
-        if task.death.unkillable > 0 { // FIXME(#7544): || self.indestructible
+        if task.death.unkillable > 0 {
             Right(Unkillable(task))
         } else {
             rtassert!(task.death.kill_handle.is_some());
index 04ce24724484aaf54d7cffd4ebe22c990b768eb6..a3ece0c2e0a1388dd87a1ee8cbec2fa39374ed3a 100644 (file)
@@ -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 @@ fn consume(&mut self) -> 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 @@ pub fn unlinked(&mut self) {
     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 @@ pub fn supervised(&mut self) {
     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 @@ pub fn spawn(&mut self, f: ~fn()) {
         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());
+    }
+}
index 9734a41ba3b8d3260758fa2433b855957db20a70..2150c0c5ac28dc39e39c2e6af2918ce9215df96d 100644 (file)
@@ -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()
     };