diff options
| author | Dylan DPC <dylan.dpc@gmail.com> | 2020-03-21 13:06:38 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-03-21 13:06:38 +0100 |
| commit | 276b54e9c930c4ff015e1958ad1c640deffd29b2 (patch) | |
| tree | 4c1eb2b3b0d3e5b53b4a4e1ada7042001f5ed089 /src/libstd/io | |
| parent | 8deeac153fcca97f6a5185b322f8d65d59fab5f4 (diff) | |
| parent | 5edaa7eefd76d4996dcf85dfc1c1a3f737087257 (diff) | |
| download | rust-276b54e9c930c4ff015e1958ad1c640deffd29b2.tar.gz rust-276b54e9c930c4ff015e1958ad1c640deffd29b2.zip | |
Rollup merge of #69955 - alexcrichton:stderr-infallible, r=sfackler
Fix abort-on-eprintln during process shutdown This commit fixes an issue where if `eprintln!` is used in a TLS destructor it can accidentally cause the process to abort. TLS destructors are executed after `main` returns on the main thread, and at this point we've also deinitialized global `Lazy` values like those which store the `Stderr` and `Stdout` internals. This means that despite handling TLS not being accessible in `eprintln!`, we will fail due to not being able to call `stderr()`. This means that we'll double-panic quickly because panicking also attempt to write to stderr. The fix here is to reimplement the global stderr handle to avoid the need for destruction. This avoids the need for `Lazy` as well as the hidden panic inside of the `stderr` function. Overall this should improve the robustness of printing errors and/or panics in weird situations, since the `stderr` accessor should be infallible in more situations.
Diffstat (limited to 'src/libstd/io')
| -rw-r--r-- | src/libstd/io/stdio.rs | 49 |
1 files changed, 31 insertions, 18 deletions
diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 0fb0757792e..9a82ae7626d 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -6,7 +6,7 @@ use crate::cell::RefCell; use crate::fmt; use crate::io::lazy::Lazy; use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter}; -use crate::sync::{Arc, Mutex, MutexGuard}; +use crate::sync::{Arc, Mutex, MutexGuard, Once}; use crate::sys::stdio; use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; use crate::thread::LocalKey; @@ -493,7 +493,11 @@ pub fn stdout() -> Stdout { Ok(stdout) => Maybe::Real(stdout), _ => Maybe::Fake, }; - Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout)))) + unsafe { + let ret = Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout)))); + ret.init(); + return ret; + } } } @@ -520,7 +524,7 @@ impl Stdout { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn lock(&self) -> StdoutLock<'_> { - StdoutLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) } + StdoutLock { inner: self.inner.lock() } } } @@ -581,7 +585,7 @@ impl fmt::Debug for StdoutLock<'_> { /// an error. #[stable(feature = "rust1", since = "1.0.0")] pub struct Stderr { - inner: Arc<ReentrantMutex<RefCell<Maybe<StderrRaw>>>>, + inner: &'static ReentrantMutex<RefCell<Maybe<StderrRaw>>>, } /// A locked reference to the `Stderr` handle. @@ -639,19 +643,28 @@ pub struct StderrLock<'a> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn stderr() -> Stderr { - static INSTANCE: Lazy<ReentrantMutex<RefCell<Maybe<StderrRaw>>>> = Lazy::new(); - return Stderr { - inner: unsafe { INSTANCE.get(stderr_init).expect("cannot access stderr during shutdown") }, - }; - - fn stderr_init() -> Arc<ReentrantMutex<RefCell<Maybe<StderrRaw>>>> { - // This must not reentrantly access `INSTANCE` - let stderr = match stderr_raw() { - Ok(stderr) => Maybe::Real(stderr), - _ => Maybe::Fake, - }; - Arc::new(ReentrantMutex::new(RefCell::new(stderr))) - } + // Note that unlike `stdout()` we don't use `Lazy` here which registers a + // destructor. Stderr is not buffered nor does the `stderr_raw` type consume + // any owned resources, so there's no need to run any destructors at some + // point in the future. + // + // This has the added benefit of allowing `stderr` to be usable during + // process shutdown as well! + static INSTANCE: ReentrantMutex<RefCell<Maybe<StderrRaw>>> = + unsafe { ReentrantMutex::new(RefCell::new(Maybe::Fake)) }; + + // When accessing stderr we need one-time initialization of the reentrant + // mutex, followed by one-time detection of whether we actually have a + // stderr handle or not. Afterwards we can just always use the now-filled-in + // `INSTANCE` value. + static INIT: Once = Once::new(); + INIT.call_once(|| unsafe { + INSTANCE.init(); + if let Ok(stderr) = stderr_raw() { + *INSTANCE.lock().borrow_mut() = Maybe::Real(stderr); + } + }); + return Stderr { inner: &INSTANCE }; } impl Stderr { @@ -677,7 +690,7 @@ impl Stderr { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn lock(&self) -> StderrLock<'_> { - StderrLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) } + StderrLock { inner: self.inner.lock() } } } |
