mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
Auto merge of #84115 - CDirkx:rt, r=m-ou-se
Rework `init` and `cleanup` This PR reworks the code in `std` that runs before and after `main` and centralizes this code respectively in the functions `init` and `cleanup` in both `sys_common` and `sys`. This makes is easy to see what code is executed during initialization and cleanup on each platform just by looking at e.g. `sys::windows::init`. Full list of changes: - new module `rt` in `sys_common` to contain `init` and `cleanup` and the runtime macros. - `at_exit` and the mechanism to register exit handlers has been completely removed. In practice this was only used for closing sockets on windows and flushing stdout, which have been moved to `cleanup`. - <s>On windows `alloc` and `net` initialization is now done in `init`, this saves a runtime check in every allocation and network use.</s>
This commit is contained in:
commit
5da10c0121
@ -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,16 @@ 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();
|
||||
// SAFETY: Only called once during runtime initialization.
|
||||
unsafe { sys_common::rt::init(argc, argv) };
|
||||
|
||||
unsafe {
|
||||
let main_guard = sys::thread::guard::init();
|
||||
sys::stack_overflow::init();
|
||||
let exit_code = panic::catch_unwind(main);
|
||||
|
||||
// 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);
|
||||
sys_common::rt::cleanup();
|
||||
|
||||
// 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))]
|
||||
|
@ -37,7 +37,6 @@ pub mod pipe;
|
||||
#[path = "../unsupported/process.rs"]
|
||||
pub mod process;
|
||||
pub mod rwlock;
|
||||
pub mod stack_overflow;
|
||||
pub mod stdio;
|
||||
pub mod thread;
|
||||
pub mod thread_local_dtor;
|
||||
@ -96,9 +95,17 @@ pub extern "C" fn __rust_abort() {
|
||||
abort_internal();
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
pub fn init() {
|
||||
// SAFETY: must be called only once during runtime initialization.
|
||||
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
||||
pub unsafe fn init(argc: isize, argv: *const *const u8) {
|
||||
let _ = net::init();
|
||||
args::init(argc, argv);
|
||||
}
|
||||
|
||||
// SAFETY: must be called only once during runtime cleanup.
|
||||
// NOTE: this is not guaranteed to run, for example when the program aborts.
|
||||
pub unsafe fn cleanup() {
|
||||
args::cleanup();
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
|
@ -1,5 +0,0 @@
|
||||
#[inline]
|
||||
pub unsafe fn init() {}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn cleanup() {}
|
@ -23,8 +23,6 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) {
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup() {}
|
||||
|
||||
pub fn args() -> Args {
|
||||
let args = unsafe { (ARGS.load(Ordering::Relaxed) as *const ArgsStore).as_ref() };
|
||||
if let Some(args) = args { Args(args.iter()) } else { Args([].iter()) }
|
||||
|
@ -32,7 +32,6 @@ pub mod pipe;
|
||||
#[path = "../unsupported/process.rs"]
|
||||
pub mod process;
|
||||
pub mod rwlock;
|
||||
pub mod stack_overflow;
|
||||
pub mod stdio;
|
||||
pub mod thread;
|
||||
pub mod thread_local_key;
|
||||
@ -40,8 +39,17 @@ 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.
|
||||
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
||||
pub unsafe fn init(argc: isize, argv: *const *const u8) {
|
||||
unsafe {
|
||||
args::init(argc, argv);
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: must be called only once during runtime cleanup.
|
||||
// NOTE: this is not guaranteed to run, for example when the program aborts.
|
||||
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.
|
||||
|
@ -1,4 +0,0 @@
|
||||
#[cfg_attr(test, allow(dead_code))]
|
||||
pub unsafe fn init() {}
|
||||
|
||||
pub unsafe fn cleanup() {}
|
@ -44,14 +44,13 @@ 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.
|
||||
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
||||
pub unsafe fn init(argc: isize, argv: *const *const u8) {
|
||||
// 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,26 +59,24 @@ 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)] {
|
||||
// The standard fds are always available in Miri.
|
||||
unsafe fn sanitize_standard_fds() {}
|
||||
} else if #[cfg(not(any(
|
||||
target_os = "emscripten",
|
||||
target_os = "fuchsia",
|
||||
target_os = "vxworks",
|
||||
// The poll on Darwin doesn't set POLLNVAL for closed fds.
|
||||
target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "redox",
|
||||
)))] {
|
||||
// In the case when all file descriptors are open, the poll has been
|
||||
// observed to perform better than fcntl (on GNU/Linux).
|
||||
unsafe fn sanitize_standard_fds() {
|
||||
stack_overflow::init();
|
||||
args::init(argc, argv);
|
||||
|
||||
unsafe fn sanitize_standard_fds() {
|
||||
#[cfg(not(miri))]
|
||||
// The standard fds are always available in Miri.
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(not(any(
|
||||
target_os = "emscripten",
|
||||
target_os = "fuchsia",
|
||||
target_os = "vxworks",
|
||||
// The poll on Darwin doesn't set POLLNVAL for closed fds.
|
||||
target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "redox",
|
||||
)))] {
|
||||
use crate::sys::os::errno;
|
||||
let pfds: &mut [_] = &mut [
|
||||
libc::pollfd { fd: 0, events: 0, revents: 0 },
|
||||
@ -104,9 +101,7 @@ pub fn init() {
|
||||
libc::abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "redox"))] {
|
||||
unsafe fn sanitize_standard_fds() {
|
||||
} else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "redox"))] {
|
||||
use crate::sys::os::errno;
|
||||
for fd in 0..3 {
|
||||
if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF {
|
||||
@ -116,17 +111,20 @@ pub fn init() {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unsafe fn sanitize_standard_fds() {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
|
||||
unsafe fn reset_sigpipe() {
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
|
||||
assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
|
||||
}
|
||||
#[cfg(any(target_os = "emscripten", target_os = "fuchsia"))]
|
||||
unsafe fn reset_sigpipe() {}
|
||||
}
|
||||
|
||||
// SAFETY: must be called only once during runtime cleanup.
|
||||
// NOTE: this is not guaranteed to run, for example when the program aborts.
|
||||
pub unsafe fn cleanup() {
|
||||
args::cleanup();
|
||||
stack_overflow::cleanup();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
|
@ -1,8 +1,5 @@
|
||||
use crate::ffi::OsString;
|
||||
|
||||
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
|
||||
pub unsafe fn cleanup() {}
|
||||
|
||||
pub struct Args {}
|
||||
|
||||
pub fn args() -> Args {
|
||||
|
@ -10,8 +10,13 @@ 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.
|
||||
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
||||
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
|
||||
|
||||
// SAFETY: must be called only once during runtime cleanup.
|
||||
// NOTE: this is not guaranteed to run, for example when the program aborts.
|
||||
pub unsafe fn cleanup() {}
|
||||
|
||||
pub fn unsupported<T>() -> std_io::Result<T> {
|
||||
Err(unsupported_err())
|
||||
|
@ -15,7 +15,6 @@ pub mod path;
|
||||
pub mod pipe;
|
||||
pub mod process;
|
||||
pub mod rwlock;
|
||||
pub mod stack_overflow;
|
||||
pub mod stdio;
|
||||
pub mod thread;
|
||||
#[cfg(target_thread_local)]
|
||||
|
@ -1,3 +0,0 @@
|
||||
pub unsafe fn init() {}
|
||||
|
||||
pub unsafe fn cleanup() {}
|
@ -5,10 +5,6 @@ use crate::fmt;
|
||||
use crate::os::wasi::ffi::OsStrExt;
|
||||
use crate::vec;
|
||||
|
||||
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
|
||||
|
||||
pub unsafe fn cleanup() {}
|
||||
|
||||
pub struct Args {
|
||||
iter: vec::IntoIter<OsString>,
|
||||
}
|
||||
|
@ -42,8 +42,6 @@ pub mod pipe;
|
||||
pub mod process;
|
||||
#[path = "../unsupported/rwlock.rs"]
|
||||
pub mod rwlock;
|
||||
#[path = "../unsupported/stack_overflow.rs"]
|
||||
pub mod stack_overflow;
|
||||
pub mod stdio;
|
||||
pub mod thread;
|
||||
#[path = "../unsupported/thread_local_dtor.rs"]
|
||||
|
@ -2,12 +2,6 @@ use crate::ffi::OsString;
|
||||
use crate::fmt;
|
||||
use crate::vec;
|
||||
|
||||
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
|
||||
// On wasm these should always be null, so there's nothing for us to do here
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup() {}
|
||||
|
||||
pub fn args() -> Args {
|
||||
Args { iter: Vec::new().into_iter() }
|
||||
}
|
||||
|
@ -35,8 +35,6 @@ pub mod path;
|
||||
pub mod pipe;
|
||||
#[path = "../unsupported/process.rs"]
|
||||
pub mod process;
|
||||
#[path = "../unsupported/stack_overflow.rs"]
|
||||
pub mod stack_overflow;
|
||||
#[path = "../unsupported/stdio.rs"]
|
||||
pub mod stdio;
|
||||
pub mod thread;
|
||||
|
@ -14,10 +14,6 @@ use crate::vec;
|
||||
|
||||
use core::iter;
|
||||
|
||||
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
|
||||
|
||||
pub unsafe fn cleanup() {}
|
||||
|
||||
pub fn args() -> Args {
|
||||
unsafe {
|
||||
let lp_cmd_line = c::GetCommandLineW();
|
||||
|
@ -49,8 +49,17 @@ cfg_if::cfg_if! {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
pub fn init() {}
|
||||
// SAFETY: must be called only once during runtime initialization.
|
||||
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
||||
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
|
||||
stack_overflow::init();
|
||||
}
|
||||
|
||||
// SAFETY: must be called only once during runtime cleanup.
|
||||
// NOTE: this is not guaranteed to run, for example when the program aborts.
|
||||
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};
|
||||
@ -26,25 +26,30 @@ pub mod netc {
|
||||
|
||||
pub struct Socket(c::SOCKET);
|
||||
|
||||
static INIT: Once = Once::new();
|
||||
|
||||
/// Checks whether the Windows socket interface has been started already, and
|
||||
/// if not, starts it.
|
||||
pub fn init() {
|
||||
static START: Once = Once::new();
|
||||
|
||||
START.call_once(|| unsafe {
|
||||
INIT.call_once(|| unsafe {
|
||||
let mut data: c::WSADATA = mem::zeroed();
|
||||
let ret = c::WSAStartup(
|
||||
0x202, // version 2.2
|
||||
&mut data,
|
||||
);
|
||||
assert_eq!(ret, 0);
|
||||
|
||||
let _ = sys_common::at_exit(|| {
|
||||
c::WSACleanup();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub fn cleanup() {
|
||||
if INIT.is_completed() {
|
||||
// only close the socket interface if it has actually been started
|
||||
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() })
|
||||
|
@ -37,5 +37,3 @@ pub unsafe fn init() {
|
||||
// Set the thread stack guarantee for the main thread.
|
||||
let _h = Handler::new();
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup() {}
|
||||
|
@ -9,5 +9,3 @@ impl Handler {
|
||||
}
|
||||
|
||||
pub unsafe fn init() {}
|
||||
|
||||
pub unsafe fn cleanup() {}
|
||||
|
@ -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;
|
||||
@ -61,6 +32,8 @@ pub mod mutex;
|
||||
pub mod os_str_bytes;
|
||||
pub mod process;
|
||||
pub mod remutex;
|
||||
#[macro_use]
|
||||
pub mod rt;
|
||||
pub mod rwlock;
|
||||
pub mod thread;
|
||||
pub mod thread_info;
|
||||
@ -108,30 +81,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).
|
||||
|
64
library/std/src/sys_common/rt.rs
Normal file
64
library/std/src/sys_common/rt.rs
Normal file
@ -0,0 +1,64 @@
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
use crate::sync::Once;
|
||||
use crate::sys;
|
||||
use crate::sys_common::thread_info;
|
||||
use crate::thread::Thread;
|
||||
|
||||
// One-time runtime initialization.
|
||||
// Runs before `main`.
|
||||
// SAFETY: must be called only once during runtime initialization.
|
||||
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
||||
#[cfg_attr(test, allow(dead_code))]
|
||||
pub unsafe fn init(argc: isize, argv: *const *const u8) {
|
||||
unsafe {
|
||||
sys::init(argc, argv);
|
||||
|
||||
let main_guard = sys::thread::guard::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);
|
||||
}
|
||||
}
|
||||
|
||||
// One-time runtime cleanup.
|
||||
// Runs after `main` or at program exit.
|
||||
// NOTE: 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 {
|
||||
// Flush stdout and disable buffering.
|
||||
crate::io::cleanup();
|
||||
// SAFETY: Only called once during runtime cleanup.
|
||||
sys::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…
Reference in New Issue
Block a user