Skip to content

Commit ac435af

Browse files
committed
Add at_exit function rust-lang#4450
1 parent 090b247 commit ac435af

File tree

6 files changed

+157
-0
lines changed

6 files changed

+157
-0
lines changed

src/libcore/private.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ use task;
2828
use task::{TaskBuilder, atomically};
2929
use uint;
3030

31+
#[path = "private/at_exit.rs"]
32+
pub mod at_exit;
33+
3134
extern mod rustrt {
3235
#[legacy_exports];
3336
unsafe fn rust_task_weaken(ch: rust_port_id);

src/libcore/private/at_exit.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use sys;
2+
use cast;
3+
use ptr;
4+
use task;
5+
use uint;
6+
use vec;
7+
use rand;
8+
use libc::{c_void, size_t};
9+
10+
/**
11+
Register a function to be run during runtime shutdown.
12+
13+
After all non-weak tasks have exited, registered exit functions will
14+
execute, in random order, on the primary scheduler. Each function runs
15+
in its own unsupervised task.
16+
*/
17+
pub fn at_exit(f: ~fn()) unsafe {
18+
let runner: &fn(*ExitFunctions) = exit_runner;
19+
let runner_pair: sys::Closure = cast::transmute(runner);
20+
let runner_ptr = runner_pair.code;
21+
let runner_ptr = cast::transmute(runner_ptr);
22+
rustrt::rust_register_exit_function(runner_ptr, ~f);
23+
}
24+
25+
// NB: The double pointer indirection here is because ~fn() is a fat
26+
// pointer and due to FFI problems I am more comfortable making the
27+
// interface use a normal pointer
28+
extern mod rustrt {
29+
fn rust_register_exit_function(runner: *c_void, f: ~~fn());
30+
}
31+
32+
struct ExitFunctions {
33+
// The number of exit functions
34+
count: size_t,
35+
// The buffer of exit functions
36+
start: *~~fn()
37+
}
38+
39+
fn exit_runner(exit_fns: *ExitFunctions) unsafe {
40+
let exit_fns = &*exit_fns;
41+
let count = (*exit_fns).count;
42+
let start = (*exit_fns).start;
43+
44+
// NB: from_buf memcpys from the source, which will
45+
// give us ownership of the array of functions
46+
let mut exit_fns_vec = vec::from_buf(start, count as uint);
47+
// Let's not make any promises about execution order
48+
rand::Rng().shuffle_mut(exit_fns_vec);
49+
50+
debug!("running %u exit functions", exit_fns_vec.len());
51+
52+
while exit_fns_vec.is_not_empty() {
53+
match exit_fns_vec.pop() {
54+
~f => {
55+
task::task().supervised().spawn(f);
56+
}
57+
}
58+
}
59+
}
60+
61+
#[abi = "rust-intrinsic"]
62+
pub extern mod rusti {
63+
fn move_val_init<T>(dst: &mut T, -src: T);
64+
fn init<T>() -> T;
65+
}
66+
67+
#[test]
68+
fn test_at_exit() {
69+
let i = 10;
70+
do at_exit {
71+
debug!("at_exit1");
72+
assert i == 10;
73+
}
74+
}
75+
76+
#[test]
77+
fn test_at_exit_many() {
78+
let i = 10;
79+
for uint::range(20, 100) |j| {
80+
do at_exit {
81+
debug!("at_exit2");
82+
assert i == 10;
83+
assert j > i;
84+
}
85+
}
86+
}

src/rt/rust_builtin.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,11 @@ rust_raw_thread_join_delete(raw_thread *thread) {
10261026
delete thread;
10271027
}
10281028

1029+
extern "C" void
1030+
rust_register_exit_function(spawn_fn runner, fn_env_pair *f) {
1031+
rust_task *task = rust_get_current_task();
1032+
task->kernel->register_exit_function(runner, f);
1033+
}
10291034

10301035
//
10311036
// Local Variables:

src/rt/rust_kernel.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ rust_kernel::rust_kernel(rust_env *env) :
3636
non_weak_tasks(0),
3737
global_loop_chan(0),
3838
global_env_chan(0),
39+
at_exit_runner(NULL),
40+
at_exit_started(false),
3941
env(env)
4042

4143
{
@@ -427,6 +429,7 @@ rust_kernel::begin_shutdown() {
427429
}
428430
}
429431

432+
run_exit_functions();
430433
allow_scheduler_exit();
431434
end_weak_tasks();
432435
}
@@ -446,6 +449,47 @@ rust_kernel::send_to_port(rust_port_id chan, void *sptr) {
446449
}
447450
}
448451

452+
void
453+
rust_kernel::register_exit_function(spawn_fn runner, fn_env_pair *f) {
454+
scoped_lock with(at_exit_lock);
455+
456+
assert(!at_exit_started && "registering at_exit function after exit");
457+
458+
if (at_exit_runner) {
459+
assert(runner == at_exit_runner
460+
&& "there can be only one at_exit_runner");
461+
}
462+
463+
at_exit_runner = runner;
464+
at_exit_fns.push_back(f);
465+
}
466+
467+
void
468+
rust_kernel::run_exit_functions() {
469+
rust_task *task;
470+
471+
{
472+
scoped_lock with(at_exit_lock);
473+
474+
assert(!at_exit_started && "running exit functions twice?");
475+
476+
at_exit_started = true;
477+
478+
if (at_exit_runner == NULL) {
479+
return;
480+
}
481+
482+
rust_scheduler *sched = get_scheduler_by_id(main_sched_id());
483+
assert(sched);
484+
task = sched->create_task(NULL, "at_exit");
485+
486+
final_exit_fns.count = at_exit_fns.size();
487+
final_exit_fns.start = at_exit_fns.data();
488+
}
489+
490+
task->start(at_exit_runner, NULL, &final_exit_fns);
491+
}
492+
449493
//
450494
// Local Variables:
451495
// mode: C++

src/rt/rust_kernel.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include "memory_region.h"
5151
#include "rust_log.h"
5252
#include "rust_sched_reaper.h"
53+
#include "rust_type.h"
5354
#include "util/hash_map.h"
5455

5556
class rust_scheduler;
@@ -66,6 +67,13 @@ typedef intptr_t rust_port_id;
6667

6768
typedef std::map<rust_sched_id, rust_scheduler*> sched_map;
6869

70+
// This is defined as a struct only because we need a single pointer to pass
71+
// to the Rust function that runs the at_exit functions
72+
struct exit_functions {
73+
size_t count;
74+
fn_env_pair **start;
75+
};
76+
6977
class rust_kernel {
7078
memory_region _region;
7179
rust_log _log;
@@ -126,6 +134,14 @@ class rust_kernel {
126134
// Used to serialize access to getenv/setenv
127135
uintptr_t global_env_chan;
128136

137+
lock_and_signal at_exit_lock;
138+
spawn_fn at_exit_runner;
139+
bool at_exit_started;
140+
std::vector<fn_env_pair*> at_exit_fns;
141+
exit_functions final_exit_fns;
142+
143+
void run_exit_functions();
144+
129145
public:
130146
struct rust_env *env;
131147

@@ -175,6 +191,8 @@ class rust_kernel {
175191

176192
uintptr_t* get_global_loop() { return &global_loop_chan; }
177193
uintptr_t* get_global_env_chan() { return &global_env_chan; }
194+
195+
void register_exit_function(spawn_fn runner, fn_env_pair *f);
178196
};
179197

180198
template <typename T> struct kernel_owned {

src/rt/rustrt.def.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,4 @@ linenoiseHistorySave
210210
linenoiseHistoryLoad
211211
rust_raw_thread_start
212212
rust_raw_thread_join_delete
213+
rust_register_exit_function

0 commit comments

Comments
 (0)