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.
This commit is contained in:
Alex Crichton 2014-07-24 07:32:14 -07:00
parent 355c798ac3
commit 8643a0d613
4 changed files with 68 additions and 6 deletions

View file

@ -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

View file

@ -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() {

View file

@ -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();
}
}

View file

@ -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());
}