summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorBrian Anderson <banderson@mozilla.com>2012-05-15 11:34:52 -0700
committerBrian Anderson <banderson@mozilla.com>2012-05-15 16:13:42 -0700
commit7277cd71984e6a09bc2f8a8a828a5e213b485d00 (patch)
tree1bab4372580c8f39c7514923f13fef9da33219e8 /src
parent8fe0461f06ac4a7fe9705d2cd62e8bb547975c8f (diff)
downloadrust-7277cd71984e6a09bc2f8a8a828a5e213b485d00.tar.gz
rust-7277cd71984e6a09bc2f8a8a828a5e213b485d00.zip
core: Add task::unkillable
Diffstat (limited to 'src')
-rw-r--r--src/libcore/task.rs63
-rw-r--r--src/rt/rust_builtin.cpp12
-rw-r--r--src/rt/rust_task.cpp13
-rw-r--r--src/rt/rust_task.h4
-rw-r--r--src/rt/rustrt.def.in2
5 files changed, 92 insertions, 2 deletions
diff --git a/src/libcore/task.rs b/src/libcore/task.rs
index 2c91b0de011..b9a07a7625b 100644
--- a/src/libcore/task.rs
+++ b/src/libcore/task.rs
@@ -51,7 +51,7 @@ export try;
 export yield;
 export failing;
 export get_task;
-
+export unkillable;
 
 /* Data types */
 
@@ -467,6 +467,29 @@ fn get_task() -> task {
     task(rustrt::get_task_id())
 }
 
+#[doc = "
+Temporarily make the task unkillable
+
+# Example
+
+    task::unkillable {||
+        // detach / yield / destroy must all be called together
+        rustrt::rust_port_detach(po);
+        // This must not result in the current task being killed
+        task::yield();
+        rustrt::rust_port_destroy(po);
+    }
+
+"]
+unsafe fn unkillable(f: fn()) {
+    resource allow_failure(_i: ()) {
+        rustrt::rust_task_allow_kill();
+    }
+    let _allow_failure = allow_failure(());
+    rustrt::rust_task_inhibit_kill();
+    f();
+}
+
 
 /* Internal */
 
@@ -566,6 +589,8 @@ native mod rustrt {
     fn rust_task_is_unwinding(rt: *rust_task) -> bool;
     fn unsupervise();
     fn rust_osmain_sched_id() -> sched_id;
+    fn rust_task_inhibit_kill();
+    fn rust_task_allow_kill();
 }
 
 
@@ -930,3 +955,39 @@ fn test_osmain() {
     }
     comm::recv(po);
 }
+
+#[test]
+#[ignore(cfg(target_os = "win32"))]
+#[should_fail]
+fn test_unkillable() unsafe {
+    import comm::methods;
+    let po = comm::port();
+    let ch = po.chan();
+
+    // We want to do this after failing
+    spawn {||
+        iter::repeat(10u, yield);
+        ch.send(());
+    }
+
+    spawn {||
+        yield();
+        // We want to fail after the unkillable task
+        // blocks on recv
+        fail;
+    }
+
+    unkillable {||
+        let p = ~0;
+        let pp: *uint = unsafe::reinterpret_cast(p);
+        unsafe::forget(p);
+
+        // If we are killed here then the box will leak
+        po.recv();
+
+        let _p: ~int = unsafe::reinterpret_cast(pp);
+    }
+
+    // Now we can be killed
+    po.recv();
+}
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
index 4ba6c299c62..8f5c3c6fa14 100644
--- a/src/rt/rust_builtin.cpp
+++ b/src/rt/rust_builtin.cpp
@@ -805,6 +805,18 @@ rust_global_env_chan_ptr() {
     return task->kernel->get_global_env_chan();
 }
 
+extern "C" void
+rust_task_inhibit_kill() {
+    rust_task *task = rust_get_current_task();
+    task->inhibit_kill();
+}
+
+extern "C" void
+rust_task_allow_kill() {
+    rust_task *task = rust_get_current_task();
+    task->allow_kill();
+}
+
 //
 // Local Variables:
 // mode: C++
diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp
index d7c62780fbb..823937443b5 100644
--- a/src/rt/rust_task.cpp
+++ b/src/rt/rust_task.cpp
@@ -37,6 +37,7 @@ rust_task::rust_task(rust_sched_loop *sched_loop, rust_task_state state,
     cond_name("none"),
     killed(false),
     reentered_rust_stack(false),
+    disallow_kill(false),
     c_stack(NULL),
     next_c_sp(0),
     next_rust_sp(0),
@@ -211,7 +212,7 @@ rust_task::must_fail_from_being_killed() {
 bool
 rust_task::must_fail_from_being_killed_unlocked() {
     kill_lock.must_have_lock();
-    return killed && !reentered_rust_stack;
+    return killed && !reentered_rust_stack && !disallow_kill;
 }
 
 // Only run this on the rust stack
@@ -645,6 +646,16 @@ rust_task::on_rust_stack() {
     }
 }
 
+void
+rust_task::inhibit_kill() {
+    disallow_kill = true;
+}
+
+void
+rust_task::allow_kill() {
+    disallow_kill = false;
+}
+
 //
 // Local Variables:
 // mode: C++
diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h
index 5a841f00f59..1867c8f4ed8 100644
--- a/src/rt/rust_task.h
+++ b/src/rt/rust_task.h
@@ -155,6 +155,7 @@ private:
     bool killed;
     // Indicates that we've called back into Rust from C
     bool reentered_rust_stack;
+    bool disallow_kill;
 
     // The stack used for running C code, borrowed from the scheduler thread
     stk_seg *c_stack;
@@ -268,6 +269,9 @@ public:
     const char *get_cond_name() { return cond_name; }
 
     void cleanup_after_turn();
+
+    void inhibit_kill();
+    void allow_kill();
 };
 
 // FIXME: It would be really nice to be able to get rid of this.
diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in
index ae6d0521bef..345cd5f7a2e 100644
--- a/src/rt/rustrt.def.in
+++ b/src/rt/rustrt.def.in
@@ -152,3 +152,5 @@ rust_global_env_chan_ptr
 rust_port_take
 rust_port_drop
 rust_port_task
+rust_task_inhibit_kill
+rust_task_allow_kill
\ No newline at end of file