Backtrace: std and threadsafe bevy_error_panic_hook (#18235)
# Objective Make `bevy_error_panic_hook` threadsafe. As it relies on a global variable, it fails when multiple threads panic. ## Solution Switch from a global variable for storing whether an error message was printed to a thread-local one. `thread_local` is in `std`; the `backtrace` already relies on `std` APIs. It didn't depend on the `std` feature though, so I've added that. I've also put `bevy_error_panic_hook` behind the `backtrace` feature, since it relies on the thread local variable, which fixes #18231. ## Testing The following now loops instead of crashing: ```rust std:🧵:scope(|s| { use bevy_ecs::error::*; #[derive(Debug)] struct E; impl std::fmt::Display for E { fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { todo!() } } impl std::error::Error for E {} std::panic::set_hook(Box::new(bevy_error_panic_hook(|_| { unreachable!(); }))); for _ in 0..2 { s.spawn(|| { loop { let _ = std::panic::catch_unwind(|| { panic!("{:?}", BevyError::from(E)); }); } }); } }); ```
This commit is contained in:
parent
b054d116a6
commit
cdef139710
@ -37,7 +37,7 @@ reflect_functions = ["bevy_reflect", "bevy_reflect/functions"]
|
|||||||
configurable_error_handler = []
|
configurable_error_handler = []
|
||||||
|
|
||||||
## Enables automatic backtrace capturing in BevyError
|
## Enables automatic backtrace capturing in BevyError
|
||||||
backtrace = []
|
backtrace = ["std"]
|
||||||
|
|
||||||
# Debugging Features
|
# Debugging Features
|
||||||
|
|
||||||
|
|||||||
@ -117,7 +117,7 @@ impl Debug for BevyError {
|
|||||||
}
|
}
|
||||||
if !full_backtrace {
|
if !full_backtrace {
|
||||||
if std::thread::panicking() {
|
if std::thread::panicking() {
|
||||||
SKIP_NORMAL_BACKTRACE.store(1, core::sync::atomic::Ordering::Relaxed);
|
SKIP_NORMAL_BACKTRACE.set(true);
|
||||||
}
|
}
|
||||||
writeln!(f, "{FILTER_MESSAGE}")?;
|
writeln!(f, "{FILTER_MESSAGE}")?;
|
||||||
}
|
}
|
||||||
@ -132,22 +132,23 @@ impl Debug for BevyError {
|
|||||||
const FILTER_MESSAGE: &str = "note: Some \"noisy\" backtrace lines have been filtered out. Run with `BEVY_BACKTRACE=full` for a verbose backtrace.";
|
const FILTER_MESSAGE: &str = "note: Some \"noisy\" backtrace lines have been filtered out. Run with `BEVY_BACKTRACE=full` for a verbose backtrace.";
|
||||||
|
|
||||||
#[cfg(feature = "backtrace")]
|
#[cfg(feature = "backtrace")]
|
||||||
static SKIP_NORMAL_BACKTRACE: core::sync::atomic::AtomicUsize =
|
std::thread_local! {
|
||||||
core::sync::atomic::AtomicUsize::new(0);
|
static SKIP_NORMAL_BACKTRACE: core::cell::Cell<bool> =
|
||||||
|
const { core::cell::Cell::new(false) };
|
||||||
|
}
|
||||||
|
|
||||||
/// When called, this will skip the currently configured panic hook when a [`BevyError`] backtrace has already been printed.
|
/// When called, this will skip the currently configured panic hook when a [`BevyError`] backtrace has already been printed.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "backtrace")]
|
||||||
pub fn bevy_error_panic_hook(
|
pub fn bevy_error_panic_hook(
|
||||||
current_hook: impl Fn(&std::panic::PanicHookInfo),
|
current_hook: impl Fn(&std::panic::PanicHookInfo),
|
||||||
) -> impl Fn(&std::panic::PanicHookInfo) {
|
) -> impl Fn(&std::panic::PanicHookInfo) {
|
||||||
move |info| {
|
move |info| {
|
||||||
if SKIP_NORMAL_BACKTRACE.load(core::sync::atomic::Ordering::Relaxed) > 0 {
|
if SKIP_NORMAL_BACKTRACE.replace(false) {
|
||||||
if let Some(payload) = info.payload().downcast_ref::<&str>() {
|
if let Some(payload) = info.payload().downcast_ref::<&str>() {
|
||||||
std::println!("{payload}");
|
std::println!("{payload}");
|
||||||
} else if let Some(payload) = info.payload().downcast_ref::<alloc::string::String>() {
|
} else if let Some(payload) = info.payload().downcast_ref::<alloc::string::String>() {
|
||||||
std::println!("{payload}");
|
std::println!("{payload}");
|
||||||
}
|
}
|
||||||
SKIP_NORMAL_BACKTRACE.store(0, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user