diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2014-07-24 07:32:14 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2014-07-30 08:33:53 -0700 |
| commit | 8643a0d61359dfb1ebe38d4aae615e7c836b3d18 (patch) | |
| tree | cc656602f919f34db3faaa7f2d72e58b94250499 | |
| parent | 355c798ac3eba15bb2d53a6c553c6149391f9615 (diff) | |
| download | rust-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.
| -rw-r--r-- | src/libgreen/task.rs | 21 | ||||
| -rw-r--r-- | src/librustrt/bookkeeping.rs | 4 | ||||
| -rw-r--r-- | src/librustrt/task.rs | 2 | ||||
| -rw-r--r-- | src/test/run-pass/spawn-stack-too-big.rs | 47 |
4 files changed, 68 insertions, 6 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 diff --git a/src/librustrt/bookkeeping.rs b/src/librustrt/bookkeeping.rs index ba9995e34ca..dc96aecff80 100644 --- a/src/librustrt/bookkeeping.rs +++ b/src/librustrt/bookkeeping.rs @@ -26,7 +26,7 @@ use mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; 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 @@ impl Drop for Token { /// the count when dropped. pub fn increment() -> Token { let _ = unsafe { TASK_COUNT.fetch_add(1, atomics::SeqCst) }; - Token(()) + Token { _private: () } } pub fn decrement() { diff --git a/src/librustrt/task.rs b/src/librustrt/task.rs index 0f4d72c9b32..e3d9b7d136e 100644 --- a/src/librustrt/task.rs +++ b/src/librustrt/task.rs @@ -663,6 +663,6 @@ mod test { 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 index 00000000000..e1c4a480d1c --- /dev/null +++ b/src/test/run-pass/spawn-stack-too-big.rs @@ -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()); +} |
