1
Fork 0

add rust_task_is_unwinding predicate and do not kill if already unwinding

This commit is contained in:
Niko Matsakis 2012-01-09 13:47:37 -08:00
parent 005e319485
commit 110c3ccdca
5 changed files with 47 additions and 17 deletions

View file

@ -49,6 +49,7 @@ export spawn_joinable;
export spawn_connected; export spawn_connected;
export connected_fn; export connected_fn;
export connected_task; export connected_task;
export currently_unwinding;
#[abi = "rust-intrinsic"] #[abi = "rust-intrinsic"]
native mod rusti { native mod rusti {
@ -76,6 +77,8 @@ native mod rustrt {
fn migrate_alloc(alloc: *u8, target: task_id); fn migrate_alloc(alloc: *u8, target: task_id);
fn start_task(id: task, closure: *rust_closure); fn start_task(id: task, closure: *rust_closure);
fn rust_task_is_unwinding(rt: *rust_task) -> bool;
} }
/* Section: Types */ /* Section: Types */
@ -271,7 +274,7 @@ fn sleep(time_in_us: uint) {
// in a snapshot. // in a snapshot.
// #debug("yielding for %u us", time_in_us); // #debug("yielding for %u us", time_in_us);
rusti::task_sleep(task, time_in_us, killed); rusti::task_sleep(task, time_in_us, killed);
if killed { if killed && !currently_unwinding() {
fail "killed"; fail "killed";
} }
} }
@ -337,6 +340,15 @@ Unpin the current task and future child tasks
*/ */
fn unpin() { rustrt::unpin_task(); } fn unpin() { rustrt::unpin_task(); }
/*
Function: currently_unwinding()
True if we are currently unwinding after a failure.
*/
fn currently_unwinding() -> bool {
rustrt::rust_task_is_unwinding(rustrt::rust_get_task())
}
// Local Variables: // Local Variables:
// mode: rust; // mode: rust;
// fill-column: 78; // fill-column: 78;

View file

@ -245,7 +245,7 @@ rust_task::rust_task(rust_scheduler *sched, rust_task_list *state,
running_on(-1), running_on(-1),
pinned_on(-1), pinned_on(-1),
local_region(&sched->srv->local_region), local_region(&sched->srv->local_region),
failed(false), unwinding(false),
killed(false), killed(false),
propagate_failure(true), propagate_failure(true),
dynastack(this), dynastack(this),
@ -299,27 +299,27 @@ struct spawn_args {
struct cleanup_args { struct cleanup_args {
spawn_args *spargs; spawn_args *spargs;
bool failed; bool threw_exception;
}; };
void void
cleanup_task(cleanup_args *args) { cleanup_task(cleanup_args *args) {
spawn_args *a = args->spargs; spawn_args *a = args->spargs;
bool failed = args->failed; bool threw_exception = args->threw_exception;
rust_task *task = a->task; rust_task *task = a->task;
cc::do_cc(task); cc::do_cc(task);
task->die(); task->die();
if (task->killed && !failed) { if (task->killed && !threw_exception) {
LOG(task, task, "Task killed during termination"); LOG(task, task, "Task killed during termination");
failed = true; threw_exception = true;
} }
task->notify(!failed); task->notify(!threw_exception);
if (failed) { if (threw_exception) {
#ifndef __WIN32__ #ifndef __WIN32__
task->conclude_failure(); task->conclude_failure();
#else #else
@ -336,7 +336,7 @@ void task_start_wrapper(spawn_args *a)
{ {
rust_task *task = a->task; rust_task *task = a->task;
bool failed = false; bool threw_exception = false;
try { try {
// The first argument is the return pointer; as the task fn // The first argument is the return pointer; as the task fn
// must have void return type, we can safely pass 0. // must have void return type, we can safely pass 0.
@ -344,7 +344,7 @@ void task_start_wrapper(spawn_args *a)
} catch (rust_task *ex) { } catch (rust_task *ex) {
A(task->sched, ex == task, A(task->sched, ex == task,
"Expected this task to be thrown for unwinding"); "Expected this task to be thrown for unwinding");
failed = true; threw_exception = true;
} }
rust_opaque_closure* env = a->envptr; rust_opaque_closure* env = a->envptr;
@ -357,7 +357,7 @@ void task_start_wrapper(spawn_args *a)
} }
// The cleanup work needs lots of stack // The cleanup work needs lots of stack
cleanup_args ca = {a, failed}; cleanup_args ca = {a, threw_exception};
task->sched->c_context.call_shim_on_c_stack(&ca, (void*)cleanup_task); task->sched->c_context.call_shim_on_c_stack(&ca, (void*)cleanup_task);
task->ctx.next->swap(task->ctx); task->ctx.next->swap(task->ctx);
@ -437,11 +437,17 @@ rust_task::kill() {
// run_on_resume(rust_unwind_glue); // run_on_resume(rust_unwind_glue);
} }
extern "C" CDECL
bool rust_task_is_unwinding(rust_task *rt) {
return rt->unwinding;
}
void void
rust_task::fail() { rust_task::fail() {
// See note in ::kill() regarding who should call this. // See note in ::kill() regarding who should call this.
DLOG(sched, task, "task %s @0x%" PRIxPTR " failing", name, this); DLOG(sched, task, "task %s @0x%" PRIxPTR " failing", name, this);
backtrace(); backtrace();
unwinding = true;
#ifndef __WIN32__ #ifndef __WIN32__
throw this; throw this;
#else #else
@ -455,7 +461,6 @@ rust_task::fail() {
void void
rust_task::conclude_failure() { rust_task::conclude_failure() {
fail_parent(); fail_parent();
failed = true;
} }
void void

View file

@ -107,8 +107,10 @@ rust_task : public kernel_owned<rust_task>, rust_cond
memory_region local_region; memory_region local_region;
// Indicates that the task ended in failure // Indicates that fail() has been called and we are cleaning up.
bool failed; // We use this to suppress the "killed" flag during calls to yield.
bool unwinding;
// Indicates that the task was killed and needs to unwind // Indicates that the task was killed and needs to unwind
bool killed; bool killed;
bool propagate_failure; bool propagate_failure;

View file

@ -43,6 +43,7 @@ rust_ptr_eq
rust_run_program rust_run_program
rust_start rust_start
rust_getcwd rust_getcwd
rust_task_is_unwinding
rust_task_sleep rust_task_sleep
rust_get_task rust_get_task
set_min_stack set_min_stack

View file

@ -10,18 +10,26 @@ fn joinable(f: fn()) -> (task::task, comm::port<bool>) {
resource notify(data: (comm::chan<bool>, resource notify(data: (comm::chan<bool>,
@mutable bool)) { @mutable bool)) {
let (c, v) = data; let (c, v) = data;
#error["notify: task=%d v=%x unwinding=%b b=%b",
task::get_task(),
ptr::addr_of(*v) as uint,
task::currently_unwinding(),
*v];
comm::send(c, *v); comm::send(c, *v);
} }
fn wrapper(pair: (comm::chan<bool>, fn())) { fn wrapper(pair: (comm::chan<bool>, fn())) {
let (c, f) = pair; let (c, f) = pair;
let b = @mutable false; let b = @mutable false;
#error["wrapper: task=%d allocated v=%x",
task::get_task(),
ptr::addr_of(*b) as uint];
let _r = notify((c, b)); let _r = notify((c, b));
f(); f();
*b = true; *b = true;
} }
let p = comm::port(); let p = comm::port();
let c = comm::chan(p); let c = comm::chan(p);
let t = task::spawn((c, f), wrapper); let t = task::spawn {|| wrapper((c, f)) };
ret (t, p); ret (t, p);
} }
@ -34,6 +42,7 @@ fn supervised() {
// Yield to make sure the supervisor joins before we // Yield to make sure the supervisor joins before we
// fail. This is currently not needed because the supervisor // fail. This is currently not needed because the supervisor
// runs first, but I can imagine that changing. // runs first, but I can imagine that changing.
#error["supervised task=%d", task::get_task()];
task::yield(); task::yield();
fail; fail;
} }
@ -42,8 +51,9 @@ fn supervisor() {
// Unsupervise this task so the process doesn't return a failure status as // Unsupervise this task so the process doesn't return a failure status as
// a result of the main task being killed. // a result of the main task being killed.
task::unsupervise(); task::unsupervise();
let f = supervised; #error["supervisor task=%d", task::get_task()];
join(joinable(supervised)); let t = joinable(supervised);
join(t);
} }
fn main() { fn main() {