about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2012-01-09 13:47:37 -0800
committerNiko Matsakis <niko@alum.mit.edu>2012-01-09 19:53:32 -0800
commit110c3ccdcad4616fd558f248b61a4ba79d8ec86b (patch)
treee211c5b764ed6899c2ecf55047ee1a15886b722e
parent005e3194858785e9debae8116caedf4c14e3b727 (diff)
downloadrust-110c3ccdcad4616fd558f248b61a4ba79d8ec86b.tar.gz
rust-110c3ccdcad4616fd558f248b61a4ba79d8ec86b.zip
add rust_task_is_unwinding predicate and do not kill if already unwinding
-rw-r--r--src/libcore/task.rs14
-rw-r--r--src/rt/rust_task.cpp27
-rw-r--r--src/rt/rust_task.h6
-rw-r--r--src/rt/rustrt.def.in1
-rw-r--r--src/test/run-pass/task-killjoin-rsrc.rs16
5 files changed, 47 insertions, 17 deletions
diff --git a/src/libcore/task.rs b/src/libcore/task.rs
index 19e3f2b21b3..2a9a39e0aaa 100644
--- a/src/libcore/task.rs
+++ b/src/libcore/task.rs
@@ -49,6 +49,7 @@ export spawn_joinable;
 export spawn_connected;
 export connected_fn;
 export connected_task;
+export currently_unwinding;
 
 #[abi = "rust-intrinsic"]
 native mod rusti {
@@ -76,6 +77,8 @@ native mod rustrt {
     fn migrate_alloc(alloc: *u8, target: task_id);
 
     fn start_task(id: task, closure: *rust_closure);
+
+    fn rust_task_is_unwinding(rt: *rust_task) -> bool;
 }
 
 /* Section: Types */
@@ -271,7 +274,7 @@ fn sleep(time_in_us: uint) {
     // in a snapshot.
     // #debug("yielding for %u us", time_in_us);
     rusti::task_sleep(task, time_in_us, killed);
-    if killed {
+    if killed && !currently_unwinding() {
         fail "killed";
     }
 }
@@ -337,6 +340,15 @@ Unpin the current task and future child tasks
 */
 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:
 // mode: rust;
 // fill-column: 78;
diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp
index edb356a9609..eb3040ebfae 100644
--- a/src/rt/rust_task.cpp
+++ b/src/rt/rust_task.cpp
@@ -245,7 +245,7 @@ rust_task::rust_task(rust_scheduler *sched, rust_task_list *state,
     running_on(-1),
     pinned_on(-1),
     local_region(&sched->srv->local_region),
-    failed(false),
+    unwinding(false),
     killed(false),
     propagate_failure(true),
     dynastack(this),
@@ -299,27 +299,27 @@ struct spawn_args {
 
 struct cleanup_args {
     spawn_args *spargs;
-    bool failed;
+    bool threw_exception;
 };
 
 void
 cleanup_task(cleanup_args *args) {
     spawn_args *a = args->spargs;
-    bool failed = args->failed;
+    bool threw_exception = args->threw_exception;
     rust_task *task = a->task;
 
     cc::do_cc(task);
 
     task->die();
 
-    if (task->killed && !failed) {
+    if (task->killed && !threw_exception) {
         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__
         task->conclude_failure();
 #else
@@ -336,7 +336,7 @@ void task_start_wrapper(spawn_args *a)
 {
     rust_task *task = a->task;
 
-    bool failed = false;
+    bool threw_exception = false;
     try {
         // The first argument is the return pointer; as the task fn 
         // 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) {
         A(task->sched, ex == task,
           "Expected this task to be thrown for unwinding");
-        failed = true;
+        threw_exception = true;
     }
 
     rust_opaque_closure* env = a->envptr;
@@ -357,7 +357,7 @@ void task_start_wrapper(spawn_args *a)
     }
 
     // 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->ctx.next->swap(task->ctx);
@@ -437,11 +437,17 @@ rust_task::kill() {
     // run_on_resume(rust_unwind_glue);
 }
 
+extern "C" CDECL
+bool rust_task_is_unwinding(rust_task *rt) {
+    return rt->unwinding;
+}
+
 void
 rust_task::fail() {
     // See note in ::kill() regarding who should call this.
     DLOG(sched, task, "task %s @0x%" PRIxPTR " failing", name, this);
     backtrace();
+    unwinding = true;
 #ifndef __WIN32__
     throw this;
 #else
@@ -455,7 +461,6 @@ rust_task::fail() {
 void
 rust_task::conclude_failure() {
     fail_parent();
-    failed = true;
 }
 
 void
diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h
index d0e3d0e2ad6..b47b62e2d14 100644
--- a/src/rt/rust_task.h
+++ b/src/rt/rust_task.h
@@ -107,8 +107,10 @@ rust_task : public kernel_owned<rust_task>, rust_cond
 
     memory_region local_region;
 
-    // Indicates that the task ended in failure
-    bool failed;
+    // Indicates that fail() has been called and we are cleaning up.
+    // 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;
diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in
index 86b41fcb256..8a44a7ab151 100644
--- a/src/rt/rustrt.def.in
+++ b/src/rt/rustrt.def.in
@@ -43,6 +43,7 @@ rust_ptr_eq
 rust_run_program
 rust_start
 rust_getcwd
+rust_task_is_unwinding
 rust_task_sleep
 rust_get_task
 set_min_stack
diff --git a/src/test/run-pass/task-killjoin-rsrc.rs b/src/test/run-pass/task-killjoin-rsrc.rs
index 03aae01fb0b..f7829aa31df 100644
--- a/src/test/run-pass/task-killjoin-rsrc.rs
+++ b/src/test/run-pass/task-killjoin-rsrc.rs
@@ -10,18 +10,26 @@ fn joinable(f: fn()) -> (task::task, comm::port<bool>) {
     resource notify(data: (comm::chan<bool>,
                            @mutable bool)) {
         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);
     }
     fn wrapper(pair: (comm::chan<bool>, fn())) {
         let (c, f) = pair;
         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));
         f();
         *b = true;
     }
     let p = comm::port();
     let c = comm::chan(p);
-    let t = task::spawn((c, f), wrapper);
+    let t = task::spawn {|| wrapper((c, f)) };
     ret (t, p);
 }
 
@@ -34,6 +42,7 @@ fn supervised() {
     // Yield to make sure the supervisor joins before we
     // fail. This is currently not needed because the supervisor
     // runs first, but I can imagine that changing.
+    #error["supervised task=%d", task::get_task()];
     task::yield();
     fail;
 }
@@ -42,8 +51,9 @@ fn supervisor() {
     // Unsupervise this task so the process doesn't return a failure status as
     // a result of the main task being killed.
     task::unsupervise();
-    let f = supervised;
-    join(joinable(supervised));
+    #error["supervisor task=%d", task::get_task()];
+    let t = joinable(supervised);
+    join(t);
 }
 
 fn main() {