summary refs log tree commit diff
path: root/src/libstd/unstable/at_exit.rs
blob: d214b509dfbf9d2810d7f34906902b0f97f801dc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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);
        }
    }
}