Rework at_exit
to cleanup
This commit is contained in:
parent
71965ab4d0
commit
5b30662741
13 changed files with 134 additions and 196 deletions
|
@ -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,
|
||||
|
|
|
@ -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,34 +550,28 @@ pub struct StdoutLock<'a> {
|
|||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn stdout() -> Stdout {
|
||||
static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> =
|
||||
SyncOnceCell::new();
|
||||
|
||||
fn cleanup() {
|
||||
if let Some(instance) = INSTANCE.get() {
|
||||
// Flush the data and disable buffering during shutdown
|
||||
// by replacing the line writer by one with zero
|
||||
// buffering capacity.
|
||||
// We use try_lock() instead of lock(), because someone
|
||||
// might have leaked a StdoutLock, which would
|
||||
// otherwise cause a deadlock here.
|
||||
if let Some(lock) = Pin::static_ref(instance).try_lock() {
|
||||
*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())))
|
||||
},
|
||||
inner: Pin::static_ref(&STDOUT).get_or_init_pin(
|
||||
|| unsafe { ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))) },
|
||||
|mutex| unsafe { mutex.init() },
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
// We use try_lock() instead of lock(), because someone
|
||||
// might have leaked a StdoutLock, which would
|
||||
// otherwise cause a deadlock here.
|
||||
if let Some(lock) = Pin::static_ref(instance).try_lock() {
|
||||
*lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Stdout {
|
||||
/// Locks this handle to the standard output stream, returning a writable
|
||||
/// guard.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
sys_common::rt::init(argc, argv);
|
||||
let exit_code = panic::catch_unwind(main);
|
||||
sys_common::rt::cleanup();
|
||||
|
||||
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!
|
||||
let exit_code = panic::catch_unwind(main);
|
||||
|
||||
sys_common::cleanup();
|
||||
exit_code.unwrap_or(101) as isize
|
||||
}
|
||||
exit_code.unwrap_or(101) as isize
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
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"))]
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,13 +38,15 @@ pub fn init() {
|
|||
&mut data,
|
||||
);
|
||||
assert_eq!(ret, 0);
|
||||
|
||||
let _ = sys_common::at_exit(|| {
|
||||
c::WSACleanup();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub fn cleanup() {
|
||||
unsafe {
|
||||
c::WSACleanup();
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the last error from the Windows socket interface.
|
||||
fn last_error() -> io::Error {
|
||||
io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() })
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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).
|
||||
|
|
69
library/std/src/sys_common/rt.rs
Normal file
69
library/std/src/sys_common/rt.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue