about summary refs log tree commit diff
path: root/src/libgreen
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-07-24 07:32:14 -0700
committerAlex Crichton <alex@alexcrichton.com>2014-07-30 08:33:53 -0700
commit8643a0d61359dfb1ebe38d4aae615e7c836b3d18 (patch)
treecc656602f919f34db3faaa7f2d72e58b94250499 /src/libgreen
parent355c798ac3eba15bb2d53a6c553c6149391f9615 (diff)
downloadrust-8643a0d61359dfb1ebe38d4aae615e7c836b3d18.tar.gz
rust-8643a0d61359dfb1ebe38d4aae615e7c836b3d18.zip
green: Prevent runtime corruption on spawn failure
Like with libnative, when a green task failed to spawn it would leave the world
in a corrupt state where the local scheduler had been dropped as well as the
local task. Also like libnative, this patch sets up a "bomb" which when it goes
off will restore the state of the world.
Diffstat (limited to 'src/libgreen')
-rw-r--r--src/libgreen/task.rs21
1 files changed, 18 insertions, 3 deletions
diff --git a/src/libgreen/task.rs b/src/libgreen/task.rs
index 3d3b4133840..12d7b755697 100644
--- a/src/libgreen/task.rs
+++ b/src/libgreen/task.rs
@@ -442,15 +442,30 @@ impl Runtime for GreenTask {
                      f: proc():Send) {
         self.put_task(cur_task);
 
+        // First, set up a bomb which when it goes off will restore the local
+        // task unless its disarmed. This will allow us to gracefully fail from
+        // inside of `configure` which allocates a new task.
+        struct Bomb { inner: Option<Box<GreenTask>> }
+        impl Drop for Bomb {
+            fn drop(&mut self) {
+                let _ = self.inner.take().map(|task| task.put());
+            }
+        }
+        let mut bomb = Bomb { inner: Some(self) };
+
         // Spawns a task into the current scheduler. We allocate the new task's
         // stack from the scheduler's stack pool, and then configure it
         // accordingly to `opts`. Afterwards we bootstrap it immediately by
         // switching to it.
         //
         // Upon returning, our task is back in TLS and we're good to return.
-        let mut sched = self.sched.take_unwrap();
-        let sibling = GreenTask::configure(&mut sched.stack_pool, opts, f);
-        sched.run_task(self, sibling)
+        let sibling = {
+            let sched = bomb.inner.get_mut_ref().sched.get_mut_ref();
+            GreenTask::configure(&mut sched.stack_pool, opts, f)
+        };
+        let mut me = bomb.inner.take().unwrap();
+        let sched = me.sched.take().unwrap();
+        sched.run_task(me, sibling)
     }
 
     // Local I/O is provided by the scheduler's event loop