1
Fork 0

Rework at_exit to cleanup

This commit is contained in:
Christiaan Dirkx 2021-04-11 07:05:39 +02:00
parent 71965ab4d0
commit 5b30662741
13 changed files with 134 additions and 196 deletions

View file

@ -293,6 +293,10 @@ mod util;
const DEFAULT_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
pub(crate) fn cleanup() {
stdio::cleanup()
}
struct Guard<'a> {
buf: &'a mut Vec<u8>,
len: usize,

View file

@ -13,7 +13,6 @@ use crate::pin::Pin;
use crate::sync::atomic::{AtomicBool, Ordering};
use crate::sync::{Arc, Mutex, MutexGuard};
use crate::sys::stdio;
use crate::sys_common;
use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
type LocalStream = Arc<Mutex<Vec<u8>>>;
@ -508,6 +507,8 @@ pub struct StdoutLock<'a> {
inner: ReentrantMutexGuard<'a, RefCell<LineWriter<StdoutRaw>>>,
}
static STDOUT: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = SyncOnceCell::new();
/// Constructs a new handle to the standard output of the current process.
///
/// Each handle returned is a reference to a shared global buffer whose access
@ -549,11 +550,16 @@ pub struct StdoutLock<'a> {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn stdout() -> Stdout {
static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> =
SyncOnceCell::new();
Stdout {
inner: Pin::static_ref(&STDOUT).get_or_init_pin(
|| unsafe { ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))) },
|mutex| unsafe { mutex.init() },
),
}
}
fn cleanup() {
if let Some(instance) = INSTANCE.get() {
pub fn cleanup() {
if let Some(instance) = STDOUT.get() {
// Flush the data and disable buffering during shutdown
// by replacing the line writer by one with zero
// buffering capacity.
@ -564,17 +570,6 @@ pub fn stdout() -> Stdout {
*lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
}
}
}
Stdout {
inner: Pin::static_ref(&INSTANCE).get_or_init_pin(
|| unsafe {
let _ = sys_common::at_exit(cleanup);
ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw())))
},
|mutex| unsafe { mutex.init() },
),
}
}
impl Stdout {

View file

@ -1749,7 +1749,7 @@ impl Child {
/// [platform-specific behavior]: #platform-specific-behavior
#[stable(feature = "rust1", since = "1.0.0")]
pub fn exit(code: i32) -> ! {
crate::sys_common::cleanup();
crate::sys_common::rt::cleanup();
crate::sys::os::exit(code)
}

View file

@ -26,33 +26,13 @@ fn lang_start_internal(
argv: *const *const u8,
) -> isize {
use crate::panic;
use crate::sys;
use crate::sys_common;
use crate::sys_common::thread_info;
use crate::thread::Thread;
sys::init();
unsafe {
let main_guard = sys::thread::guard::init();
sys::stack_overflow::init();
// Next, set up the current Thread with the guard information we just
// created. Note that this isn't necessary in general for new threads,
// but we just do this to name the main thread and to give it correct
// info about the stack bounds.
let thread = Thread::new(Some("main".to_owned()));
thread_info::set(main_guard, thread);
// Store our args if necessary in a squirreled away location
sys::args::init(argc, argv);
// Let's run some code!
sys_common::rt::init(argc, argv);
let exit_code = panic::catch_unwind(main);
sys_common::rt::cleanup();
sys_common::cleanup();
exit_code.unwrap_or(101) as isize
}
}
#[cfg(not(test))]

View file

@ -96,11 +96,14 @@ pub extern "C" fn __rust_abort() {
abort_internal();
}
#[cfg(not(test))]
pub fn init() {
// SAFETY: must be called only once during runtime initialization.
pub unsafe fn init() {
let _ = net::init();
}
// SAFETY: must be called only once during runtime cleanup.
pub unsafe fn cleanup() {}
#[cfg(not(test))]
#[no_mangle]
pub unsafe extern "C" fn runtime_entry(

View file

@ -40,8 +40,11 @@ pub mod time;
pub use crate::sys_common::os_str_bytes as os_str;
#[cfg(not(test))]
pub fn init() {}
// SAFETY: must be called only once during runtime initialization.
pub unsafe fn init() {}
// SAFETY: must be called only once during runtime cleanup.
pub unsafe fn cleanup() {}
/// This function is used to implement functionality that simply doesn't exist.
/// Programs relying on this functionality will need to deal with the error.

View file

@ -44,14 +44,12 @@ pub mod time;
pub use crate::sys_common::os_str_bytes as os_str;
#[cfg(not(test))]
pub fn init() {
// SAFETY: must be called only once during runtime initialization.
pub unsafe fn init() {
// The standard streams might be closed on application startup. To prevent
// std::io::{stdin, stdout,stderr} objects from using other unrelated file
// resources opened later, we reopen standards streams when they are closed.
unsafe {
sanitize_standard_fds();
}
// By default, some platforms will send a *signal* when an EPIPE error
// would otherwise be delivered. This runtime doesn't install a SIGPIPE
@ -60,9 +58,7 @@ pub fn init() {
//
// Hence, we set SIGPIPE to ignore when the program starts up in order
// to prevent this problem.
unsafe {
reset_sigpipe();
}
cfg_if::cfg_if! {
if #[cfg(miri)] {
@ -129,6 +125,9 @@ pub fn init() {
unsafe fn reset_sigpipe() {}
}
// SAFETY: must be called only once during runtime cleanup.
pub unsafe fn cleanup() {}
#[cfg(target_os = "android")]
pub use crate::sys::android::signal;
#[cfg(not(target_os = "android"))]

View file

@ -10,8 +10,11 @@ pub use crate::sys_common::os_str_bytes as os_str;
// spec definition?
use crate::os::raw::c_char;
#[cfg(not(test))]
pub fn init() {}
// SAFETY: must be called only once during runtime initialization.
pub unsafe fn init() {}
// SAFETY: must be called only once during runtime cleanup.
pub unsafe fn cleanup() {}
pub fn unsupported<T>() -> std_io::Result<T> {
Err(unsupported_err())

View file

@ -49,8 +49,13 @@ cfg_if::cfg_if! {
}
}
#[cfg(not(test))]
pub fn init() {}
// SAFETY: must be called only once during runtime initialization.
pub unsafe fn init() {}
// SAFETY: must be called only once during runtime cleanup.
pub unsafe fn cleanup() {
net::cleanup();
}
pub fn decode_error_kind(errno: i32) -> ErrorKind {
match errno as c::DWORD {

View file

@ -9,7 +9,7 @@ use crate::sync::Once;
use crate::sys;
use crate::sys::c;
use crate::sys_common::net;
use crate::sys_common::{self, AsInner, FromInner, IntoInner};
use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::time::Duration;
use libc::{c_int, c_long, c_ulong, c_void};
@ -38,11 +38,13 @@ pub fn init() {
&mut data,
);
assert_eq!(ret, 0);
});
}
let _ = sys_common::at_exit(|| {
pub fn cleanup() {
unsafe {
c::WSACleanup();
});
});
}
}
/// Returns the last error from the Windows socket interface.

View file

@ -1,74 +0,0 @@
//! Implementation of running at_exit routines
//!
//! Documentation can be found on the `rt::at_exit` function.
use crate::mem;
use crate::ptr;
use crate::sys_common::mutex::StaticMutex;
type Queue = Vec<Box<dyn FnOnce()>>;
// NB these are specifically not types from `std::sync` as they currently rely
// on poisoning and this module needs to operate at a lower level than requiring
// the thread infrastructure to be in place (useful on the borders of
// initialization/destruction).
// It is UB to attempt to acquire this mutex reentrantly!
static LOCK: StaticMutex = StaticMutex::new();
static mut QUEUE: *mut Queue = ptr::null_mut();
const DONE: *mut Queue = 1_usize as *mut _;
// The maximum number of times the cleanup routines will be run. While running
// the at_exit closures new ones may be registered, and this count is the number
// of times the new closures will be allowed to register successfully. After
// this number of iterations all new registrations will return `false`.
const ITERS: usize = 10;
unsafe fn init() -> bool {
if QUEUE.is_null() {
let state: Box<Queue> = box Vec::new();
QUEUE = Box::into_raw(state);
} else if QUEUE == DONE {
// can't re-init after a cleanup
return false;
}
true
}
pub fn cleanup() {
for i in 1..=ITERS {
unsafe {
let queue = {
let _guard = LOCK.lock();
mem::replace(&mut QUEUE, if i == ITERS { DONE } else { ptr::null_mut() })
};
// make sure we're not recursively cleaning up
assert!(queue != DONE);
// If we never called init, not need to cleanup!
if !queue.is_null() {
let queue: Box<Queue> = Box::from_raw(queue);
for to_run in *queue {
// We are not holding any lock, so reentrancy is fine.
to_run();
}
}
}
}
}
pub fn push(f: Box<dyn FnOnce()>) -> bool {
unsafe {
let _guard = LOCK.lock();
if init() {
// We are just moving `f` around, not calling it.
// There is no possibility of reentrancy here.
(*QUEUE).push(f);
true
} else {
false
}
}
}

View file

@ -20,35 +20,6 @@
#[cfg(test)]
mod tests;
use crate::sync::Once;
use crate::sys;
macro_rules! rtabort {
($($t:tt)*) => (crate::sys_common::util::abort(format_args!($($t)*)))
}
macro_rules! rtassert {
($e:expr) => {
if !$e {
rtabort!(concat!("assertion failed: ", stringify!($e)));
}
};
}
#[allow(unused_macros)] // not used on all platforms
macro_rules! rtunwrap {
($ok:ident, $e:expr) => {
match $e {
$ok(v) => v,
ref err => {
let err = err.as_ref().map(drop); // map Ok/Some which might not be Debug
rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err)
}
}
};
}
pub mod at_exit_imp;
pub mod backtrace;
pub mod bytestring;
pub mod condvar;
@ -62,6 +33,8 @@ pub mod os_str_bytes;
pub mod poison;
pub mod process;
pub mod remutex;
#[macro_use]
pub mod rt;
pub mod rwlock;
pub mod thread;
pub mod thread_info;
@ -109,30 +82,6 @@ pub trait FromInner<Inner> {
fn from_inner(inner: Inner) -> Self;
}
/// Enqueues a procedure to run when the main thread exits.
///
/// Currently these closures are only run once the main *Rust* thread exits.
/// Once the `at_exit` handlers begin running, more may be enqueued, but not
/// infinitely so. Eventually a handler registration will be forced to fail.
///
/// Returns `Ok` if the handler was successfully registered, meaning that the
/// closure will be run once the main thread exits. Returns `Err` to indicate
/// that the closure could not be registered, meaning that it is not scheduled
/// to be run.
pub fn at_exit<F: FnOnce() + Send + 'static>(f: F) -> Result<(), ()> {
if at_exit_imp::push(Box::new(f)) { Ok(()) } else { Err(()) }
}
/// One-time runtime cleanup.
pub fn cleanup() {
static CLEANUP: Once = Once::new();
CLEANUP.call_once(|| unsafe {
sys::args::cleanup();
sys::stack_overflow::cleanup();
at_exit_imp::cleanup();
});
}
// Computes (value*numer)/denom without overflow, as long as both
// (numer*denom) and the overall result fit into i64 (which is the case
// for our time conversions).

View file

@ -0,0 +1,69 @@
use crate::sync::Once;
use crate::sys;
use crate::sys_common::thread_info;
use crate::thread::Thread;
// One-time runtime initialization.
// Runs before `main`.
#[cfg_attr(test, allow(dead_code))]
pub fn init(argc: isize, argv: *const *const u8) {
static INIT: Once = Once::new();
INIT.call_once(|| unsafe {
// SAFETY: Only called once during runtime initialization.
sys::init();
let main_guard = sys::thread::guard::init();
sys::stack_overflow::init();
// Next, set up the current Thread with the guard information we just
// created. Note that this isn't necessary in general for new threads,
// but we just do this to name the main thread and to give it correct
// info about the stack bounds.
let thread = Thread::new(Some("main".to_owned()));
thread_info::set(main_guard, thread);
// Store our args if necessary in a squirreled away location
sys::args::init(argc, argv);
});
}
// One-time runtime cleanup.
// Runs after `main` or at program exit. Note however that this is not guaranteed to run,
// for example when the program aborts.
#[cfg_attr(test, allow(dead_code))]
pub fn cleanup() {
static CLEANUP: Once = Once::new();
CLEANUP.call_once(|| unsafe {
// SAFETY: Only called once during runtime cleanup.
sys::cleanup();
sys::args::cleanup();
sys::stack_overflow::cleanup();
// Flush stdout and disable buffering.
crate::io::cleanup();
});
}
macro_rules! rtabort {
($($t:tt)*) => (crate::sys_common::util::abort(format_args!($($t)*)))
}
macro_rules! rtassert {
($e:expr) => {
if !$e {
rtabort!(concat!("assertion failed: ", stringify!($e)));
}
};
}
#[allow(unused_macros)] // not used on all platforms
macro_rules! rtunwrap {
($ok:ident, $e:expr) => {
match $e {
$ok(v) => v,
ref err => {
let err = err.as_ref().map(drop); // map Ok/Some which might not be Debug
rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err)
}
}
};
}