about summary refs log tree commit diff
path: root/src/libstd/unstable/at_exit.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/unstable/at_exit.rs')
-rw-r--r--src/libstd/unstable/at_exit.rs100
1 files changed, 100 insertions, 0 deletions
diff --git a/src/libstd/unstable/at_exit.rs b/src/libstd/unstable/at_exit.rs
new file mode 100644
index 00000000000..d214b509dfb
--- /dev/null
+++ b/src/libstd/unstable/at_exit.rs
@@ -0,0 +1,100 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use cast;
+use libc::size_t;
+use rand::RngUtil;
+use rand;
+use sys;
+use task;
+use vec;
+
+#[cfg(test)] use uint;
+
+/**
+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
+mod rustrt {
+    use libc::c_void;
+
+    pub extern {
+        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) {
+    let exit_fns = unsafe { &*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 = unsafe { vec::from_buf(start, count as uint) };
+    // Let's not make any promises about execution order
+    let mut rng = rand::rng();
+    rng.shuffle_mut(exit_fns_vec);
+
+    debug!("running %u exit functions", exit_fns_vec.len());
+
+    while !exit_fns_vec.is_empty() {
+        match exit_fns_vec.pop() {
+            ~f => {
+                let mut task = task::task();
+                task.supervised();
+                task.spawn(f);
+            }
+        }
+    }
+}
+
+#[test]
+fn test_at_exit() {
+    let i = 10;
+    do at_exit {
+        debug!("at_exit1");
+        assert_eq!(i, 10);
+    }
+}
+
+#[test]
+fn test_at_exit_many() {
+    let i = 10;
+    for uint::range(20, 100) |j| {
+        do at_exit {
+            debug!("at_exit2");
+            assert_eq!(i, 10);
+            assert!(j > i);
+        }
+    }
+}