]> git.lizzy.rs Git - rust.git/commitdiff
green: Prevent runtime corruption on spawn failure
authorAlex Crichton <alex@alexcrichton.com>
Thu, 24 Jul 2014 14:32:14 +0000 (07:32 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Wed, 30 Jul 2014 15:33:53 +0000 (08:33 -0700)
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.

src/libgreen/task.rs
src/librustrt/bookkeeping.rs
src/librustrt/task.rs
src/test/run-pass/spawn-stack-too-big.rs [new file with mode: 0644]

index 3d3b413384050f37e6cd6057c5b43e53118db332..12d7b75569782f18ff52408d5b880e31e45aee2c 100644 (file)
@@ -442,15 +442,30 @@ fn spawn_sibling(mut self: Box<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
index ba9995e34ca3a149d2ef9ec9c3165eaf1e4ab967..dc96aecff8017c7c858934ad3ebb56668f4fb79e 100644 (file)
@@ -26,7 +26,7 @@
 static mut TASK_COUNT: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT;
 static mut TASK_LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT;
 
-pub struct Token(());
+pub struct Token { _private: () }
 
 impl Drop for Token {
     fn drop(&mut self) { decrement() }
@@ -36,7 +36,7 @@ fn drop(&mut self) { decrement() }
 /// the count when dropped.
 pub fn increment() -> Token {
     let _ = unsafe { TASK_COUNT.fetch_add(1, atomics::SeqCst) };
-    Token(())
+    Token { _private: () }
 }
 
 pub fn decrement() {
index 0f4d72c9b3268a86ec6dd07e7d414f0874d597f4..e3d9b7d136ec2e05b6b16816e89dbb29d35418d9 100644 (file)
@@ -663,6 +663,6 @@ fn drop_new_task_ok() {
     fn block_and_wake() {
         let task = box Task::new();
         let mut task = BlockedTask::block(task).wake().unwrap();
-        task.destroy();
+        task.drop();
     }
 }
diff --git a/src/test/run-pass/spawn-stack-too-big.rs b/src/test/run-pass/spawn-stack-too-big.rs
new file mode 100644 (file)
index 0000000..e1c4a48
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-macos apparently gargantuan mmap requests are ok?
+
+#![feature(phase)]
+
+#[phase(plugin)]
+extern crate green;
+extern crate native;
+
+use std::task::TaskBuilder;
+use native::NativeTaskBuilder;
+
+green_start!(main)
+
+fn main() {
+    test();
+
+    let (tx, rx) = channel();
+    TaskBuilder::new().native().spawn(proc() {
+        tx.send(test());
+    });
+    rx.recv();
+}
+
+#[cfg(not(target_word_size = "64"))]
+fn test() {}
+
+#[cfg(target_word_size = "64")]
+fn test() {
+    let (tx, rx) = channel();
+    spawn(proc() {
+        TaskBuilder::new().stack_size(1024 * 1024 * 1024 * 64).spawn(proc() {
+        });
+        tx.send(());
+    });
+
+    assert!(rx.recv_opt().is_err());
+}