Rollup merge of #122882 - Zoxc:panic-output-panic, r=Amanieu

Avoid a panic in `set_output_capture` in the default panic handler

This avoid a panic in the default panic handler by not using `set_output_capture` as `OUTPUT_CAPTURE.with` may panic once `OUTPUT_CAPTURE` is dropped.

A new non-panicking `try_set_output_capture` variant of `set_output_capture` is added for use in the default panic handler.
This commit is contained in:
Matthias Krüger 2024-04-11 22:38:53 +02:00 committed by GitHub
commit 1e99af514b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 29 additions and 9 deletions

View file

@ -311,14 +311,14 @@
#[unstable(feature = "raw_os_error_ty", issue = "107792")]
pub use self::error::RawOsError;
pub(crate) use self::stdio::attempt_print_to_stderr;
#[unstable(feature = "internal_output_capture", issue = "none")]
#[doc(no_inline, hidden)]
pub use self::stdio::set_output_capture;
#[stable(feature = "is_terminal", since = "1.70.0")]
pub use self::stdio::IsTerminal;
#[unstable(feature = "print_internals", issue = "none")]
#[doc(hidden)]
pub use self::stdio::{_eprint, _print};
#[unstable(feature = "internal_output_capture", issue = "none")]
#[doc(no_inline, hidden)]
pub use self::stdio::{set_output_capture, try_set_output_capture};
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::{
buffered::{BufReader, BufWriter, IntoInnerError, LineWriter},

View file

@ -15,6 +15,7 @@
use crate::sync::atomic::{AtomicBool, Ordering};
use crate::sync::{Arc, Mutex, MutexGuard, OnceLock, ReentrantLock, ReentrantLockGuard};
use crate::sys::stdio;
use crate::thread::AccessError;
type LocalStream = Arc<Mutex<Vec<u8>>>;
@ -1064,12 +1065,31 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
)]
#[doc(hidden)]
pub fn set_output_capture(sink: Option<LocalStream>) -> Option<LocalStream> {
try_set_output_capture(sink).expect(
"cannot access a Thread Local Storage value \
during or after destruction",
)
}
/// Tries to set the thread-local output capture buffer and returns the old one.
/// This may fail once thread-local destructors are called. It's used in panic
/// handling instead of `set_output_capture`.
#[unstable(
feature = "internal_output_capture",
reason = "this function is meant for use in the test crate \
and may disappear in the future",
issue = "none"
)]
#[doc(hidden)]
pub fn try_set_output_capture(
sink: Option<LocalStream>,
) -> Result<Option<LocalStream>, AccessError> {
if sink.is_none() && !OUTPUT_CAPTURE_USED.load(Ordering::Relaxed) {
// OUTPUT_CAPTURE is definitely None since OUTPUT_CAPTURE_USED is false.
return None;
return Ok(None);
}
OUTPUT_CAPTURE_USED.store(true, Ordering::Relaxed);
OUTPUT_CAPTURE.with(move |slot| slot.replace(sink))
OUTPUT_CAPTURE.try_with(move |slot| slot.replace(sink))
}
/// Write `args` to the capture buffer if enabled and possible, or `global_s`

View file

@ -24,11 +24,11 @@
use crate::thread;
#[cfg(not(test))]
use crate::io::set_output_capture;
use crate::io::try_set_output_capture;
// make sure to use the stderr output configured
// by libtest in the real copy of std
#[cfg(test)]
use realstd::io::set_output_capture;
use realstd::io::try_set_output_capture;
// Binary interface to the panic runtime that the standard library depends on.
//
@ -284,9 +284,9 @@ fn default_hook(info: &PanicInfo<'_>) {
}
};
if let Some(local) = set_output_capture(None) {
if let Ok(Some(local)) = try_set_output_capture(None) {
write(&mut *local.lock().unwrap_or_else(|e| e.into_inner()));
set_output_capture(Some(local));
try_set_output_capture(Some(local)).ok();
} else if let Some(mut out) = panic_output() {
write(&mut out);
}