]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | //! Implementation of running at_exit routines |
2 | //! | |
3 | //! Documentation can be found on the `rt::at_exit` function. | |
4 | ||
532ac7d7 | 5 | use crate::mem; |
dfeec247 | 6 | use crate::ptr; |
1b1a35ee | 7 | use crate::sys_common::mutex::StaticMutex; |
1a4d82fc | 8 | |
48663c56 | 9 | type Queue = Vec<Box<dyn FnOnce()>>; |
1a4d82fc JJ |
10 | |
11 | // NB these are specifically not types from `std::sync` as they currently rely | |
12 | // on poisoning and this module needs to operate at a lower level than requiring | |
13 | // the thread infrastructure to be in place (useful on the borders of | |
14 | // initialization/destruction). | |
1b1a35ee XL |
15 | // It is UB to attempt to acquire this mutex reentrantly! |
16 | static LOCK: StaticMutex = StaticMutex::new(); | |
e9174d1e | 17 | static mut QUEUE: *mut Queue = ptr::null_mut(); |
1a4d82fc | 18 | |
94b46f34 XL |
19 | const DONE: *mut Queue = 1_usize as *mut _; |
20 | ||
c34b1796 AL |
21 | // The maximum number of times the cleanup routines will be run. While running |
22 | // the at_exit closures new ones may be registered, and this count is the number | |
23 | // of times the new closures will be allowed to register successfully. After | |
24 | // this number of iterations all new registrations will return `false`. | |
25 | const ITERS: usize = 10; | |
26 | ||
27 | unsafe fn init() -> bool { | |
1a4d82fc JJ |
28 | if QUEUE.is_null() { |
29 | let state: Box<Queue> = box Vec::new(); | |
62682a34 | 30 | QUEUE = Box::into_raw(state); |
94b46f34 | 31 | } else if QUEUE == DONE { |
1a4d82fc | 32 | // can't re-init after a cleanup |
dfeec247 | 33 | return false; |
1a4d82fc JJ |
34 | } |
35 | ||
e9174d1e | 36 | true |
1a4d82fc JJ |
37 | } |
38 | ||
39 | pub fn cleanup() { | |
94b46f34 | 40 | for i in 1..=ITERS { |
c34b1796 | 41 | unsafe { |
94b46f34 XL |
42 | let queue = { |
43 | let _guard = LOCK.lock(); | |
44 | mem::replace(&mut QUEUE, if i == ITERS { DONE } else { ptr::null_mut() }) | |
45 | }; | |
1a4d82fc | 46 | |
c34b1796 | 47 | // make sure we're not recursively cleaning up |
94b46f34 | 48 | assert!(queue != DONE); |
1a4d82fc | 49 | |
c34b1796 | 50 | // If we never called init, not need to cleanup! |
94b46f34 | 51 | if !queue.is_null() { |
c34b1796 AL |
52 | let queue: Box<Queue> = Box::from_raw(queue); |
53 | for to_run in *queue { | |
b7449926 | 54 | // We are not holding any lock, so reentrancy is fine. |
c34b1796 AL |
55 | to_run(); |
56 | } | |
1a4d82fc JJ |
57 | } |
58 | } | |
59 | } | |
60 | } | |
61 | ||
48663c56 | 62 | pub fn push(f: Box<dyn FnOnce()>) -> bool { |
1a4d82fc | 63 | unsafe { |
94b46f34 | 64 | let _guard = LOCK.lock(); |
c34b1796 | 65 | if init() { |
b7449926 XL |
66 | // We are just moving `f` around, not calling it. |
67 | // There is no possibility of reentrancy here. | |
c34b1796 | 68 | (*QUEUE).push(f); |
94b46f34 | 69 | true |
c34b1796 | 70 | } else { |
94b46f34 | 71 | false |
c34b1796 | 72 | } |
1a4d82fc JJ |
73 | } |
74 | } |