Skip to content

Commit 8e86e02

Browse files
committed
Auto merge of rust-lang#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>
2 parents 25376f5 + 490acb0 commit 8e86e02

26 files changed

+175
-262
lines changed

Diff for: std/src/io/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,10 @@ mod util;
293293

294294
const DEFAULT_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
295295

296+
pub(crate) fn cleanup() {
297+
stdio::cleanup()
298+
}
299+
296300
struct Guard<'a> {
297301
buf: &'a mut Vec<u8>,
298302
len: usize,

Diff for: std/src/io/stdio.rs

+18-23
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use crate::pin::Pin;
1313
use crate::sync::atomic::{AtomicBool, Ordering};
1414
use crate::sync::{Arc, Mutex, MutexGuard};
1515
use crate::sys::stdio;
16-
use crate::sys_common;
1716
use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
1817

1918
type LocalStream = Arc<Mutex<Vec<u8>>>;
@@ -508,6 +507,8 @@ pub struct StdoutLock<'a> {
508507
inner: ReentrantMutexGuard<'a, RefCell<LineWriter<StdoutRaw>>>,
509508
}
510509

510+
static STDOUT: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = SyncOnceCell::new();
511+
511512
/// Constructs a new handle to the standard output of the current process.
512513
///
513514
/// Each handle returned is a reference to a shared global buffer whose access
@@ -549,34 +550,28 @@ pub struct StdoutLock<'a> {
549550
/// ```
550551
#[stable(feature = "rust1", since = "1.0.0")]
551552
pub fn stdout() -> Stdout {
552-
static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> =
553-
SyncOnceCell::new();
554-
555-
fn cleanup() {
556-
if let Some(instance) = INSTANCE.get() {
557-
// Flush the data and disable buffering during shutdown
558-
// by replacing the line writer by one with zero
559-
// buffering capacity.
560-
// We use try_lock() instead of lock(), because someone
561-
// might have leaked a StdoutLock, which would
562-
// otherwise cause a deadlock here.
563-
if let Some(lock) = Pin::static_ref(instance).try_lock() {
564-
*lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
565-
}
566-
}
567-
}
568-
569553
Stdout {
570-
inner: Pin::static_ref(&INSTANCE).get_or_init_pin(
571-
|| unsafe {
572-
let _ = sys_common::at_exit(cleanup);
573-
ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw())))
574-
},
554+
inner: Pin::static_ref(&STDOUT).get_or_init_pin(
555+
|| unsafe { ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))) },
575556
|mutex| unsafe { mutex.init() },
576557
),
577558
}
578559
}
579560

561+
pub fn cleanup() {
562+
if let Some(instance) = STDOUT.get() {
563+
// Flush the data and disable buffering during shutdown
564+
// by replacing the line writer by one with zero
565+
// buffering capacity.
566+
// We use try_lock() instead of lock(), because someone
567+
// might have leaked a StdoutLock, which would
568+
// otherwise cause a deadlock here.
569+
if let Some(lock) = Pin::static_ref(instance).try_lock() {
570+
*lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
571+
}
572+
}
573+
}
574+
580575
impl Stdout {
581576
/// Locks this handle to the standard output stream, returning a writable
582577
/// guard.

Diff for: std/src/process.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1749,7 +1749,7 @@ impl Child {
17491749
/// [platform-specific behavior]: #platform-specific-behavior
17501750
#[stable(feature = "rust1", since = "1.0.0")]
17511751
pub fn exit(code: i32) -> ! {
1752-
crate::sys_common::cleanup();
1752+
crate::sys_common::rt::cleanup();
17531753
crate::sys::os::exit(code)
17541754
}
17551755

Diff for: std/src/rt.rs

+5-22
Original file line numberDiff line numberDiff line change
@@ -26,33 +26,16 @@ fn lang_start_internal(
2626
argv: *const *const u8,
2727
) -> isize {
2828
use crate::panic;
29-
use crate::sys;
3029
use crate::sys_common;
31-
use crate::sys_common::thread_info;
32-
use crate::thread::Thread;
3330

34-
sys::init();
31+
// SAFETY: Only called once during runtime initialization.
32+
unsafe { sys_common::rt::init(argc, argv) };
3533

36-
unsafe {
37-
let main_guard = sys::thread::guard::init();
38-
sys::stack_overflow::init();
34+
let exit_code = panic::catch_unwind(main);
3935

40-
// Next, set up the current Thread with the guard information we just
41-
// created. Note that this isn't necessary in general for new threads,
42-
// but we just do this to name the main thread and to give it correct
43-
// info about the stack bounds.
44-
let thread = Thread::new(Some("main".to_owned()));
45-
thread_info::set(main_guard, thread);
36+
sys_common::rt::cleanup();
4637

47-
// Store our args if necessary in a squirreled away location
48-
sys::args::init(argc, argv);
49-
50-
// Let's run some code!
51-
let exit_code = panic::catch_unwind(main);
52-
53-
sys_common::cleanup();
54-
exit_code.unwrap_or(101) as isize
55-
}
38+
exit_code.unwrap_or(101) as isize
5639
}
5740

5841
#[cfg(not(test))]

Diff for: std/src/sys/hermit/mod.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ pub mod pipe;
3737
#[path = "../unsupported/process.rs"]
3838
pub mod process;
3939
pub mod rwlock;
40-
pub mod stack_overflow;
4140
pub mod stdio;
4241
pub mod thread;
4342
pub mod thread_local_dtor;
@@ -96,9 +95,17 @@ pub extern "C" fn __rust_abort() {
9695
abort_internal();
9796
}
9897

99-
#[cfg(not(test))]
100-
pub fn init() {
98+
// SAFETY: must be called only once during runtime initialization.
99+
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
100+
pub unsafe fn init(argc: isize, argv: *const *const u8) {
101101
let _ = net::init();
102+
args::init(argc, argv);
103+
}
104+
105+
// SAFETY: must be called only once during runtime cleanup.
106+
// NOTE: this is not guaranteed to run, for example when the program aborts.
107+
pub unsafe fn cleanup() {
108+
args::cleanup();
102109
}
103110

104111
#[cfg(not(test))]

Diff for: std/src/sys/hermit/stack_overflow.rs

-5
This file was deleted.

Diff for: std/src/sys/sgx/args.rs

-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) {
2323
}
2424
}
2525

26-
pub unsafe fn cleanup() {}
27-
2826
pub fn args() -> Args {
2927
let args = unsafe { (ARGS.load(Ordering::Relaxed) as *const ArgsStore).as_ref() };
3028
if let Some(args) = args { Args(args.iter()) } else { Args([].iter()) }

Diff for: std/src/sys/sgx/mod.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,24 @@ pub mod pipe;
3232
#[path = "../unsupported/process.rs"]
3333
pub mod process;
3434
pub mod rwlock;
35-
pub mod stack_overflow;
3635
pub mod stdio;
3736
pub mod thread;
3837
pub mod thread_local_key;
3938
pub mod time;
4039

4140
pub use crate::sys_common::os_str_bytes as os_str;
4241

43-
#[cfg(not(test))]
44-
pub fn init() {}
42+
// SAFETY: must be called only once during runtime initialization.
43+
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
44+
pub unsafe fn init(argc: isize, argv: *const *const u8) {
45+
unsafe {
46+
args::init(argc, argv);
47+
}
48+
}
49+
50+
// SAFETY: must be called only once during runtime cleanup.
51+
// NOTE: this is not guaranteed to run, for example when the program aborts.
52+
pub unsafe fn cleanup() {}
4553

4654
/// This function is used to implement functionality that simply doesn't exist.
4755
/// Programs relying on this functionality will need to deal with the error.

Diff for: std/src/sys/sgx/stack_overflow.rs

-4
This file was deleted.

Diff for: std/src/sys/unix/mod.rs

+30-32
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,13 @@ pub mod time;
4444

4545
pub use crate::sys_common::os_str_bytes as os_str;
4646

47-
#[cfg(not(test))]
48-
pub fn init() {
47+
// SAFETY: must be called only once during runtime initialization.
48+
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
49+
pub unsafe fn init(argc: isize, argv: *const *const u8) {
4950
// The standard streams might be closed on application startup. To prevent
5051
// std::io::{stdin, stdout,stderr} objects from using other unrelated file
5152
// resources opened later, we reopen standards streams when they are closed.
52-
unsafe {
53-
sanitize_standard_fds();
54-
}
53+
sanitize_standard_fds();
5554

5655
// By default, some platforms will send a *signal* when an EPIPE error
5756
// would otherwise be delivered. This runtime doesn't install a SIGPIPE
@@ -60,26 +59,24 @@ pub fn init() {
6059
//
6160
// Hence, we set SIGPIPE to ignore when the program starts up in order
6261
// to prevent this problem.
63-
unsafe {
64-
reset_sigpipe();
65-
}
62+
reset_sigpipe();
63+
64+
stack_overflow::init();
65+
args::init(argc, argv);
6666

67-
cfg_if::cfg_if! {
68-
if #[cfg(miri)] {
69-
// The standard fds are always available in Miri.
70-
unsafe fn sanitize_standard_fds() {}
71-
} else if #[cfg(not(any(
72-
target_os = "emscripten",
73-
target_os = "fuchsia",
74-
target_os = "vxworks",
75-
// The poll on Darwin doesn't set POLLNVAL for closed fds.
76-
target_os = "macos",
77-
target_os = "ios",
78-
target_os = "redox",
79-
)))] {
80-
// In the case when all file descriptors are open, the poll has been
81-
// observed to perform better than fcntl (on GNU/Linux).
82-
unsafe fn sanitize_standard_fds() {
67+
unsafe fn sanitize_standard_fds() {
68+
#[cfg(not(miri))]
69+
// The standard fds are always available in Miri.
70+
cfg_if::cfg_if! {
71+
if #[cfg(not(any(
72+
target_os = "emscripten",
73+
target_os = "fuchsia",
74+
target_os = "vxworks",
75+
// The poll on Darwin doesn't set POLLNVAL for closed fds.
76+
target_os = "macos",
77+
target_os = "ios",
78+
target_os = "redox",
79+
)))] {
8380
use crate::sys::os::errno;
8481
let pfds: &mut [_] = &mut [
8582
libc::pollfd { fd: 0, events: 0, revents: 0 },
@@ -104,9 +101,7 @@ pub fn init() {
104101
libc::abort();
105102
}
106103
}
107-
}
108-
} else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "redox"))] {
109-
unsafe fn sanitize_standard_fds() {
104+
} else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "redox"))] {
110105
use crate::sys::os::errno;
111106
for fd in 0..3 {
112107
if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF {
@@ -116,17 +111,20 @@ pub fn init() {
116111
}
117112
}
118113
}
119-
} else {
120-
unsafe fn sanitize_standard_fds() {}
121114
}
122115
}
123116

124-
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
125117
unsafe fn reset_sigpipe() {
118+
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
126119
assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
127120
}
128-
#[cfg(any(target_os = "emscripten", target_os = "fuchsia"))]
129-
unsafe fn reset_sigpipe() {}
121+
}
122+
123+
// SAFETY: must be called only once during runtime cleanup.
124+
// NOTE: this is not guaranteed to run, for example when the program aborts.
125+
pub unsafe fn cleanup() {
126+
args::cleanup();
127+
stack_overflow::cleanup();
130128
}
131129

132130
#[cfg(target_os = "android")]

Diff for: std/src/sys/unsupported/args.rs

-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
use crate::ffi::OsString;
22

3-
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
4-
pub unsafe fn cleanup() {}
5-
63
pub struct Args {}
74

85
pub fn args() -> Args {

Diff for: std/src/sys/unsupported/common.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@ pub use crate::sys_common::os_str_bytes as os_str;
1010
// spec definition?
1111
use crate::os::raw::c_char;
1212

13-
#[cfg(not(test))]
14-
pub fn init() {}
13+
// SAFETY: must be called only once during runtime initialization.
14+
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
15+
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
16+
17+
// SAFETY: must be called only once during runtime cleanup.
18+
// NOTE: this is not guaranteed to run, for example when the program aborts.
19+
pub unsafe fn cleanup() {}
1520

1621
pub fn unsupported<T>() -> std_io::Result<T> {
1722
Err(unsupported_err())

Diff for: std/src/sys/unsupported/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ pub mod path;
1515
pub mod pipe;
1616
pub mod process;
1717
pub mod rwlock;
18-
pub mod stack_overflow;
1918
pub mod stdio;
2019
pub mod thread;
2120
#[cfg(target_thread_local)]

Diff for: std/src/sys/unsupported/stack_overflow.rs

-3
This file was deleted.

Diff for: std/src/sys/wasi/args.rs

-4
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@ use crate::fmt;
55
use crate::os::wasi::ffi::OsStrExt;
66
use crate::vec;
77

8-
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
9-
10-
pub unsafe fn cleanup() {}
11-
128
pub struct Args {
139
iter: vec::IntoIter<OsString>,
1410
}

Diff for: std/src/sys/wasi/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ pub mod pipe;
4242
pub mod process;
4343
#[path = "../unsupported/rwlock.rs"]
4444
pub mod rwlock;
45-
#[path = "../unsupported/stack_overflow.rs"]
46-
pub mod stack_overflow;
4745
pub mod stdio;
4846
pub mod thread;
4947
#[path = "../unsupported/thread_local_dtor.rs"]

Diff for: std/src/sys/wasm/args.rs

-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@ use crate::ffi::OsString;
22
use crate::fmt;
33
use crate::vec;
44

5-
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
6-
// On wasm these should always be null, so there's nothing for us to do here
7-
}
8-
9-
pub unsafe fn cleanup() {}
10-
115
pub fn args() -> Args {
126
Args { iter: Vec::new().into_iter() }
137
}

Diff for: std/src/sys/wasm/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ pub mod path;
3535
pub mod pipe;
3636
#[path = "../unsupported/process.rs"]
3737
pub mod process;
38-
#[path = "../unsupported/stack_overflow.rs"]
39-
pub mod stack_overflow;
4038
#[path = "../unsupported/stdio.rs"]
4139
pub mod stdio;
4240
pub mod thread;

Diff for: std/src/sys/windows/args.rs

-4
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ use crate::vec;
1414

1515
use core::iter;
1616

17-
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
18-
19-
pub unsafe fn cleanup() {}
20-
2117
pub fn args() -> Args {
2218
unsafe {
2319
let lp_cmd_line = c::GetCommandLineW();

0 commit comments

Comments
 (0)