Skip to content

Commit bab15f7

Browse files
committed
Remove std::io::lazy::Lazy in favour of SyncOnceCell
The (internal) std::io::lazy::Lazy was used to lazily initialize the stdout and stdin buffers (and mutexes). It uses atexit() to register a destructor to flush the streams on exit, and mark the streams as 'closed'. Using the stream afterwards would result in a panic. Stdout uses a LineWriter which contains a BufWriter that will flush the buffer on drop. This one is important to be executed during shutdown, to make sure no buffered output is lost. It also forbids access to stdout afterwards, since the buffer is already flushed and gone. Stdin uses a BufReader, which does not implement Drop. It simply forgets any previously read data that was not read from the buffer yet. This means that in the case of stdin, the atexit() function's only effect is making stdin inaccessible to the program, such that later accesses result in a panic. This is uncessary, as it'd have been safe to access stdin during shutdown of the program. --- This change removes the entire io::lazy module in favour of SyncOnceCell. SyncOnceCell's fast path is much faster (a single atomic operation) than locking a sys_common::Mutex on every access like Lazy did. However, SyncOnceCell does not use atexit() to drop the contained object during shutdown. As noted above, this is not a problem for stdin. It simply means stdin is now usable during shutdown. The atexit() call for stdout is moved to the stdio module. Unlike the now-removed Lazy struct, SyncOnceCell does not have a 'gone and unusable' state that panics. Instead of adding this again, this simply replaces the buffer with one with zero capacity. This effectively flushes the old buffer *and* makes any writes afterwards pass through directly without touching a buffer, making print!() available during shutdown without panicking.
1 parent 4eff9b0 commit bab15f7

File tree

3 files changed

+40
-98
lines changed

3 files changed

+40
-98
lines changed

library/std/src/io/lazy.rs

-63
This file was deleted.

library/std/src/io/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,6 @@ mod buffered;
285285
mod cursor;
286286
mod error;
287287
mod impls;
288-
mod lazy;
289288
pub mod prelude;
290289
mod stdio;
291290
mod util;

library/std/src/io/stdio.rs

+40-34
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ use crate::io::prelude::*;
77

88
use crate::cell::RefCell;
99
use crate::fmt;
10-
use crate::io::lazy::Lazy;
1110
use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter};
12-
use crate::sync::{Arc, Mutex, MutexGuard, Once};
11+
use crate::lazy::SyncOnceCell;
12+
use crate::sync::{Arc, Mutex, MutexGuard};
1313
use crate::sys::stdio;
14+
use crate::sys_common;
1415
use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
1516
use crate::thread::LocalKey;
1617

@@ -292,15 +293,13 @@ pub struct StdinLock<'a> {
292293
/// ```
293294
#[stable(feature = "rust1", since = "1.0.0")]
294295
pub fn stdin() -> Stdin {
295-
static INSTANCE: Lazy<Mutex<BufReader<StdinRaw>>> = Lazy::new();
296-
return Stdin {
297-
inner: unsafe { INSTANCE.get(stdin_init).expect("cannot access stdin during shutdown") },
298-
};
299-
300-
fn stdin_init() -> Arc<Mutex<BufReader<StdinRaw>>> {
301-
// This must not reentrantly access `INSTANCE`
302-
let stdin = stdin_raw();
303-
Arc::new(Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, stdin)))
296+
static INSTANCE: SyncOnceCell<Arc<Mutex<BufReader<StdinRaw>>>> = SyncOnceCell::new();
297+
Stdin {
298+
inner: INSTANCE
299+
.get_or_init(|| {
300+
Arc::new(Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, stdin_raw())))
301+
})
302+
.clone(),
304303
}
305304
}
306305

@@ -534,19 +533,27 @@ pub struct StdoutLock<'a> {
534533
/// ```
535534
#[stable(feature = "rust1", since = "1.0.0")]
536535
pub fn stdout() -> Stdout {
537-
static INSTANCE: Lazy<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = Lazy::new();
538-
return Stdout {
539-
inner: unsafe { INSTANCE.get(stdout_init).expect("cannot access stdout during shutdown") },
540-
};
541-
542-
fn stdout_init() -> Arc<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> {
543-
// This must not reentrantly access `INSTANCE`
544-
let stdout = stdout_raw();
545-
unsafe {
546-
let ret = Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout))));
547-
ret.init();
548-
ret
549-
}
536+
static INSTANCE: SyncOnceCell<Arc<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>>> =
537+
SyncOnceCell::new();
538+
Stdout {
539+
inner: INSTANCE
540+
.get_or_init(|| unsafe {
541+
let _ = sys_common::at_exit(|| {
542+
if let Some(instance) = INSTANCE.get() {
543+
// Flush the data and disable buffering during shutdown
544+
// by replacing the line writer by one with zero
545+
// buffering capacity.
546+
// We use try_lock() instead of lock(), because someone
547+
// might have leaked a StdoutLock, which would
548+
// otherwise cause a deadlock here.
549+
if let Some(lock) = instance.try_lock() {
550+
*lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
551+
}
552+
}
553+
});
554+
Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))))
555+
})
556+
.clone(),
550557
}
551558
}
552559

@@ -714,16 +721,15 @@ pub fn stderr() -> Stderr {
714721
//
715722
// This has the added benefit of allowing `stderr` to be usable during
716723
// process shutdown as well!
717-
static INSTANCE: ReentrantMutex<RefCell<StderrRaw>> =
718-
unsafe { ReentrantMutex::new(RefCell::new(stderr_raw())) };
719-
720-
// When accessing stderr we need one-time initialization of the reentrant
721-
// mutex. Afterwards we can just always use the now-filled-in `INSTANCE` value.
722-
static INIT: Once = Once::new();
723-
INIT.call_once(|| unsafe {
724-
INSTANCE.init();
725-
});
726-
Stderr { inner: &INSTANCE }
724+
static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<StderrRaw>>> = SyncOnceCell::new();
725+
726+
Stderr {
727+
inner: INSTANCE.get_or_init(|| unsafe {
728+
let r = ReentrantMutex::new(RefCell::new(stderr_raw()));
729+
r.init();
730+
r
731+
}),
732+
}
727733
}
728734

729735
impl Stderr {

0 commit comments

Comments
 (0)