about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libcore/private.rs3
-rw-r--r--src/libcore/private/at_exit.rs86
-rw-r--r--src/rt/rust_builtin.cpp5
-rw-r--r--src/rt/rust_kernel.cpp44
-rw-r--r--src/rt/rust_kernel.h18
-rw-r--r--src/rt/rustrt.def.in1
6 files changed, 157 insertions, 0 deletions
diff --git a/src/libcore/private.rs b/src/libcore/private.rs
index d3002ba9316..48489dab488 100644
--- a/src/libcore/private.rs
+++ b/src/libcore/private.rs
@@ -28,6 +28,9 @@ use task;
 use task::{TaskBuilder, atomically};
 use uint;
 
+#[path = "private/at_exit.rs"]
+pub mod at_exit;
+
 extern mod rustrt {
     #[legacy_exports];
     unsafe fn rust_task_weaken(ch: rust_port_id);
diff --git a/src/libcore/private/at_exit.rs b/src/libcore/private/at_exit.rs
new file mode 100644
index 00000000000..7ac252ea102
--- /dev/null
+++ b/src/libcore/private/at_exit.rs
@@ -0,0 +1,86 @@
+use sys;
+use cast;
+use ptr;
+use task;
+use uint;
+use vec;
+use rand;
+use libc::{c_void, size_t};
+
+/**
+Register a function to be run during runtime shutdown.
+
+After all non-weak tasks have exited, registered exit functions will
+execute, in random order, on the primary scheduler. Each function runs
+in its own unsupervised task.
+*/
+pub fn at_exit(f: ~fn()) unsafe {
+    let runner: &fn(*ExitFunctions) = exit_runner;
+    let runner_pair: sys::Closure = cast::transmute(runner);
+    let runner_ptr = runner_pair.code;
+    let runner_ptr = cast::transmute(runner_ptr);
+    rustrt::rust_register_exit_function(runner_ptr, ~f);
+}
+
+// NB: The double pointer indirection here is because ~fn() is a fat
+// pointer and due to FFI problems I am more comfortable making the
+// interface use a normal pointer
+extern mod rustrt {
+    fn rust_register_exit_function(runner: *c_void, f: ~~fn());
+}
+
+struct ExitFunctions {
+    // The number of exit functions
+    count: size_t,
+    // The buffer of exit functions
+    start: *~~fn()
+}
+
+fn exit_runner(exit_fns: *ExitFunctions) unsafe {
+    let exit_fns = &*exit_fns;
+    let count = (*exit_fns).count;
+    let start = (*exit_fns).start;
+
+    // NB: from_buf memcpys from the source, which will
+    // give us ownership of the array of functions
+    let mut exit_fns_vec = vec::from_buf(start, count as uint);
+    // Let's not make any promises about execution order
+    rand::Rng().shuffle_mut(exit_fns_vec);
+
+    debug!("running %u exit functions", exit_fns_vec.len());
+
+    while exit_fns_vec.is_not_empty() {
+        match exit_fns_vec.pop() {
+            ~f => {
+                task::task().supervised().spawn(f);
+            }
+        }
+    }
+}
+
+#[abi = "rust-intrinsic"]
+pub extern mod rusti {
+    fn move_val_init<T>(dst: &mut T, -src: T);
+    fn init<T>() -> T;
+}
+
+#[test]
+fn test_at_exit() {
+    let i = 10;
+    do at_exit {
+        debug!("at_exit1");
+        assert i == 10;
+    }
+}
+
+#[test]
+fn test_at_exit_many() {
+    let i = 10;
+    for uint::range(20, 100) |j| {
+        do at_exit {
+            debug!("at_exit2");
+            assert i == 10;
+            assert j > i;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
index cbc58e85db6..a37aea13e40 100644
--- a/src/rt/rust_builtin.cpp
+++ b/src/rt/rust_builtin.cpp
@@ -1026,6 +1026,11 @@ rust_raw_thread_join_delete(raw_thread *thread) {
     delete thread;
 }
 
+extern "C" void
+rust_register_exit_function(spawn_fn runner, fn_env_pair *f) {
+    rust_task *task = rust_get_current_task();
+    task->kernel->register_exit_function(runner, f);
+}
 
 //
 // Local Variables:
diff --git a/src/rt/rust_kernel.cpp b/src/rt/rust_kernel.cpp
index cc98b474ee3..3042b006a92 100644
--- a/src/rt/rust_kernel.cpp
+++ b/src/rt/rust_kernel.cpp
@@ -36,6 +36,8 @@ rust_kernel::rust_kernel(rust_env *env) :
     non_weak_tasks(0),
     global_loop_chan(0),
     global_env_chan(0),
+    at_exit_runner(NULL),
+    at_exit_started(false),
     env(env)
 
 {
@@ -427,6 +429,7 @@ rust_kernel::begin_shutdown() {
         }
     }
 
+    run_exit_functions();
     allow_scheduler_exit();
     end_weak_tasks();
 }
@@ -446,6 +449,47 @@ rust_kernel::send_to_port(rust_port_id chan, void *sptr) {
     }
 }
 
+void
+rust_kernel::register_exit_function(spawn_fn runner, fn_env_pair *f) {
+    scoped_lock with(at_exit_lock);
+
+    assert(!at_exit_started && "registering at_exit function after exit");
+
+    if (at_exit_runner) {
+        assert(runner == at_exit_runner
+               && "there can be only one at_exit_runner");
+    }
+
+    at_exit_runner = runner;
+    at_exit_fns.push_back(f);
+}
+
+void
+rust_kernel::run_exit_functions() {
+    rust_task *task;
+
+    {
+        scoped_lock with(at_exit_lock);
+
+        assert(!at_exit_started && "running exit functions twice?");
+
+        at_exit_started = true;
+
+        if (at_exit_runner == NULL) {
+            return;
+        }
+
+        rust_scheduler *sched = get_scheduler_by_id(main_sched_id());
+        assert(sched);
+        task = sched->create_task(NULL, "at_exit");
+
+        final_exit_fns.count = at_exit_fns.size();
+        final_exit_fns.start = at_exit_fns.data();
+    }
+
+    task->start(at_exit_runner, NULL, &final_exit_fns);
+}
+
 //
 // Local Variables:
 // mode: C++
diff --git a/src/rt/rust_kernel.h b/src/rt/rust_kernel.h
index 13fd8934172..b1548e92cdb 100644
--- a/src/rt/rust_kernel.h
+++ b/src/rt/rust_kernel.h
@@ -50,6 +50,7 @@
 #include "memory_region.h"
 #include "rust_log.h"
 #include "rust_sched_reaper.h"
+#include "rust_type.h"
 #include "util/hash_map.h"
 
 class rust_scheduler;
@@ -66,6 +67,13 @@ typedef intptr_t rust_port_id;
 
 typedef std::map<rust_sched_id, rust_scheduler*> sched_map;
 
+// This is defined as a struct only because we need a single pointer to pass
+// to the Rust function that runs the at_exit functions
+struct exit_functions {
+    size_t count;
+    fn_env_pair **start;
+};
+
 class rust_kernel {
     memory_region _region;
     rust_log _log;
@@ -126,6 +134,14 @@ class rust_kernel {
     // Used to serialize access to getenv/setenv
     uintptr_t global_env_chan;
 
+    lock_and_signal at_exit_lock;
+    spawn_fn at_exit_runner;
+    bool at_exit_started;
+    std::vector<fn_env_pair*> at_exit_fns;
+    exit_functions final_exit_fns;
+
+    void run_exit_functions();
+
 public:
     struct rust_env *env;
 
@@ -175,6 +191,8 @@ public:
 
     uintptr_t* get_global_loop() { return &global_loop_chan; }
     uintptr_t* get_global_env_chan() { return &global_env_chan; }
+
+    void register_exit_function(spawn_fn runner, fn_env_pair *f);
 };
 
 template <typename T> struct kernel_owned {
diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in
index cce4e411e02..719505079e6 100644
--- a/src/rt/rustrt.def.in
+++ b/src/rt/rustrt.def.in
@@ -210,3 +210,4 @@ linenoiseHistorySave
 linenoiseHistoryLoad
 rust_raw_thread_start
 rust_raw_thread_join_delete
+rust_register_exit_function