Auto merge of #89011 - bjorn3:restructure_rt, r=dtolnay

Restructure std::rt

These changes should reduce binary size slightly while at the same slightly improving performance of startup, thread spawning and `std:🧵:current()`. I haven't verified if the compiler is able to optimize some of these cases already, but at least for some others the compiler is unable to do these optimizations as they slightly change behavior in cases where program startup would crash anyway by omitting a backtrace and panic location.

I can remove 6f6bb16 if preferred.
This commit is contained in:
bors 2021-09-29 17:58:08 +00:00
commit 11491938f8
8 changed files with 112 additions and 114 deletions

View File

@ -520,20 +520,20 @@ pub mod task {
pub use alloc::task::*;
}
// Platform-abstraction modules
// The runtime entry point and a few unstable public functions used by the
// compiler
#[macro_use]
mod sys_common;
pub mod rt;
// Platform-abstraction modules
mod sys;
mod sys_common;
pub mod alloc;
// Private support modules
mod panicking;
// The runtime entry point and a few unstable public functions used by the
// compiler
pub mod rt;
#[path = "../../backtrace/src/lib.rs"]
#[allow(dead_code, unused_attributes)]
mod backtrace_rs;

View File

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

View File

@ -13,11 +13,93 @@
issue = "none"
)]
#![doc(hidden)]
#![deny(unsafe_op_in_unsafe_fn)]
#![allow(unused_macros)]
use crate::ffi::CString;
// Re-export some of our utilities which are expected by other crates.
pub use crate::panicking::{begin_panic, begin_panic_fmt, panic_count};
pub use core::panicking::panic_display;
use crate::sync::Once;
use crate::sys;
use crate::sys_common::thread_info;
use crate::thread::Thread;
// Prints to the "panic output", depending on the platform this may be:
// - the standard error output
// - some dedicated platform specific output
// - nothing (so this macro is a no-op)
macro_rules! rtprintpanic {
($($t:tt)*) => {
if let Some(mut out) = crate::sys::stdio::panic_output() {
let _ = crate::io::Write::write_fmt(&mut out, format_args!($($t)*));
}
}
}
macro_rules! rtabort {
($($t:tt)*) => {
{
rtprintpanic!("fatal runtime error: {}\n", format_args!($($t)*));
crate::sys::abort_internal();
}
}
}
macro_rules! rtassert {
($e:expr) => {
if !$e {
rtabort!(concat!("assertion failed: ", stringify!($e)));
}
};
}
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)
}
}
};
}
// 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))]
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(rtunwrap!(Ok, CString::new("main"))));
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.
pub(crate) 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();
});
}
// To reduce the generated code of the new `lang_start`, this function is doing
// the real work.
#[cfg(not(test))]
@ -26,7 +108,7 @@ fn lang_start_internal(
argc: isize,
argv: *const *const u8,
) -> Result<isize, !> {
use crate::{mem, panic, sys, sys_common};
use crate::{mem, panic};
let rt_abort = move |e| {
mem::forget(e);
rtabort!("initialization or cleanup bug");
@ -42,14 +124,14 @@ fn lang_start_internal(
// prevent libstd from accidentally introducing a panic to these functions. Another is from
// user code from `main` or, more nefariously, as described in e.g. issue #86030.
// SAFETY: Only called once during runtime initialization.
panic::catch_unwind(move || unsafe { sys_common::rt::init(argc, argv) }).map_err(rt_abort)?;
panic::catch_unwind(move || unsafe { init(argc, argv) }).map_err(rt_abort)?;
let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize)
.map_err(move |e| {
mem::forget(e);
rtprintpanic!("drop of the panic payload panicked");
sys::abort_internal()
});
panic::catch_unwind(sys_common::rt::cleanup).map_err(rt_abort)?;
panic::catch_unwind(cleanup).map_err(rt_abort)?;
ret_code
}

View File

@ -120,7 +120,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) {
unsafe fn reset_sigpipe() {
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
rtassert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
}
}

View File

@ -28,8 +28,6 @@ pub mod memchr;
pub mod mutex;
pub mod process;
pub mod remutex;
#[macro_use]
pub mod rt;
pub mod rwlock;
pub mod thread;
pub mod thread_info;

View File

@ -1,81 +0,0 @@
#![deny(unsafe_op_in_unsafe_fn)]
#![allow(unused_macros)]
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();
});
}
// Prints to the "panic output", depending on the platform this may be:
// - the standard error output
// - some dedicated platform specific output
// - nothing (so this macro is a no-op)
macro_rules! rtprintpanic {
($($t:tt)*) => {
if let Some(mut out) = crate::sys::stdio::panic_output() {
let _ = crate::io::Write::write_fmt(&mut out, format_args!($($t)*));
}
}
}
macro_rules! rtabort {
($($t:tt)*) => {
{
rtprintpanic!("fatal runtime error: {}\n", format_args!($($t)*));
crate::sys::abort_internal();
}
}
}
macro_rules! rtassert {
($e:expr) => {
if !$e {
rtabort!(concat!("assertion failed: ", stringify!($e)));
}
};
}
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)
}
}
};
}

View File

@ -1,4 +1,5 @@
#![allow(dead_code)] // stack_guard isn't used right now on all platforms
#![allow(unused_unsafe)] // thread_local with `const {}` triggers this liny
use crate::cell::RefCell;
use crate::sys::thread::guard::Guard;
@ -9,7 +10,7 @@ struct ThreadInfo {
thread: Thread,
}
thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = RefCell::new(None) }
thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = const { RefCell::new(None) } }
impl ThreadInfo {
fn with<R, F>(f: F) -> Option<R>
@ -17,12 +18,13 @@ impl ThreadInfo {
F: FnOnce(&mut ThreadInfo) -> R,
{
THREAD_INFO
.try_with(move |c| {
if c.borrow().is_none() {
*c.borrow_mut() =
Some(ThreadInfo { stack_guard: None, thread: Thread::new(None) })
}
f(c.borrow_mut().as_mut().unwrap())
.try_with(move |thread_info| {
let mut thread_info = thread_info.borrow_mut();
let thread_info = thread_info.get_or_insert_with(|| ThreadInfo {
stack_guard: None,
thread: Thread::new(None),
});
f(thread_info)
})
.ok()
}
@ -37,10 +39,9 @@ pub fn stack_guard() -> Option<Guard> {
}
pub fn set(stack_guard: Option<Guard>, thread: Thread) {
THREAD_INFO.with(|c| assert!(c.borrow().is_none()));
THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo { stack_guard, thread }));
}
pub fn reset_guard(stack_guard: Option<Guard>) {
THREAD_INFO.with(move |c| c.borrow_mut().as_mut().unwrap().stack_guard = stack_guard);
THREAD_INFO.with(move |thread_info| {
let mut thread_info = thread_info.borrow_mut();
rtassert!(thread_info.is_none());
*thread_info = Some(ThreadInfo { stack_guard, thread });
});
}

View File

@ -457,7 +457,9 @@ impl Builder {
let stack_size = stack_size.unwrap_or_else(thread::min_stack);
let my_thread = Thread::new(name);
let my_thread = Thread::new(name.map(|name| {
CString::new(name).expect("thread name may not contain interior null bytes")
}));
let their_thread = my_thread.clone();
let my_packet: Arc<UnsafeCell<Option<Result<T>>>> = Arc::new(UnsafeCell::new(None));
@ -1073,12 +1075,8 @@ pub struct Thread {
impl Thread {
// Used only internally to construct a thread object without spawning
// Panics if the name contains nuls.
pub(crate) fn new(name: Option<String>) -> Thread {
let cname =
name.map(|n| CString::new(n).expect("thread name may not contain interior null bytes"));
Thread {
inner: Arc::new(Inner { name: cname, id: ThreadId::new(), parker: Parker::new() }),
}
pub(crate) fn new(name: Option<CString>) -> Thread {
Thread { inner: Arc::new(Inner { name, id: ThreadId::new(), parker: Parker::new() }) }
}
/// Atomically makes the handle's token available if it is not already.