diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 918630b18f6..628389f10ce 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -576,7 +576,7 @@ port_recv(uintptr_t *dptr, rust_port *port, // If this task has been killed then we're not going to bother // blocking, we have to unwind. - if (task->killed) { + if (task->must_fail_from_being_killed()) { *killed = true; return; } diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp index 65568ad8ed5..14c223ddb91 100644 --- a/src/rt/rust_task.cpp +++ b/src/rt/rust_task.cpp @@ -84,11 +84,12 @@ rust_task::rust_task(rust_task_thread *thread, rust_task_list *state, local_region(&thread->srv->local_region), boxed(&local_region), unwinding(false), - killed(false), propagate_failure(true), dynastack(this), cc_counter(0), total_stack_sz(0), + killed(false), + reentered_rust_stack(false), c_stack(NULL), next_c_sp(0), next_rust_sp(0) @@ -240,17 +241,22 @@ void rust_task::start() transition(&thread->newborn_tasks, &thread->running_tasks); } +bool +rust_task::must_fail_from_being_killed() { + return killed && !reentered_rust_stack; +} + // Only run this on the rust stack void rust_task::yield(bool *killed) { - if (this->killed) { + if (must_fail_from_being_killed()) { *killed = true; } // Return to the scheduler. ctx.next->swap(ctx); - if (this->killed) { + if (must_fail_from_being_killed()) { *killed = true; } } diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h index 13cede08c72..0a1b02037a2 100644 --- a/src/rt/rust_task.h +++ b/src/rt/rust_task.h @@ -37,6 +37,7 @@ typedef unsigned long task_result; #define tr_failure 1 struct spawn_args; +struct cleanup_args; // std::lib::task::task_notification // @@ -88,8 +89,6 @@ rust_task : public kernel_owned, rust_cond // We use this to suppress the "killed" flag during calls to yield. bool unwinding; - // Indicates that the task was killed and needs to unwind - bool killed; bool propagate_failure; lock_and_signal lock; @@ -107,6 +106,11 @@ rust_task : public kernel_owned, rust_cond private: + // Indicates that the task was killed and needs to unwind + bool killed; + // Indicates that we've called back into Rust from C + bool reentered_rust_stack; + // The stack used for running C code, borrowed from the scheduler thread stk_seg *c_stack; uintptr_t next_c_sp; @@ -123,6 +127,7 @@ private: void return_c_stack(); friend void task_start_wrapper(spawn_args *a); + friend void cleanup_task(cleanup_args *a); public: @@ -162,6 +167,10 @@ public: // Fail this task (assuming caller-on-stack is different task). void kill(); + // Indicates that we've been killed and now is an apropriate + // time to fail as a result + bool must_fail_from_being_killed(); + // Fail self, assuming caller-on-stack is this task. void fail(); void conclude_failure(); @@ -262,6 +271,9 @@ rust_task::call_on_rust_stack(void *args, void *fn_ptr) { // I(thread, !on_rust_stack()); I(thread, next_rust_sp); + bool had_reentered_rust_stack = reentered_rust_stack; + reentered_rust_stack = true; + uintptr_t prev_c_sp = next_c_sp; next_c_sp = get_sp(); @@ -270,6 +282,7 @@ rust_task::call_on_rust_stack(void *args, void *fn_ptr) { __morestack(args, fn_ptr, sp); next_c_sp = prev_c_sp; + reentered_rust_stack = had_reentered_rust_stack; } inline void diff --git a/src/test/run-fail/crust-fail.rs b/src/test/run-fail/crust-fail.rs new file mode 100644 index 00000000000..aafd70bb84f --- /dev/null +++ b/src/test/run-fail/crust-fail.rs @@ -0,0 +1,31 @@ +// error-pattern:explicit failure +// Testing that runtime failure doesn't cause callbacks to abort abnormally. +// Instead the failure will be delivered after the callbacks return. + +native mod rustrt { + fn rust_dbg_call(cb: *u8, + data: ctypes::uintptr_t) -> ctypes::uintptr_t; +} + +crust fn cb(data: ctypes::uintptr_t) -> ctypes::uintptr_t { + if data == 1u { + data + } else { + count(data - 1u) + count(data - 1u) + } +} + +fn count(n: uint) -> uint { + task::yield(); + rustrt::rust_dbg_call(cb, n) +} + +fn main() { + iter::repeat(10u) {|| + task::spawn {|| + let result = count(5u); + #debug("result = %?", result); + fail; + }; + } +}