Skip to content

Commit 3e678e2

Browse files
review comments
1 parent b9c7c84 commit 3e678e2

File tree

2 files changed

+23
-19
lines changed

2 files changed

+23
-19
lines changed

crates/bevy_ecs/src/error/handler.rs

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,20 @@ impl ErrorContext {
7676
}
7777

7878
mod global_error_handler {
79-
use super::{BevyError, ErrorContext};
79+
use super::{panic, BevyError, ErrorContext};
8080
use bevy_platform_support::sync::atomic::{
81-
AtomicPtr,
81+
AtomicBool, AtomicPtr,
8282
Ordering::{AcqRel, Acquire, Relaxed},
8383
};
8484

85+
// The default global error handler, cast to a data pointer as Rust doesn't
86+
// currently have a way to express atomic function pointers.
87+
// Should we add support for a platform on which function pointers and data pointers
88+
// have different sizes, the transmutation back will fail to compile. In that case,
89+
// we can replace the atomic pointer with a regular pointer protected by a `RwLock`
90+
// on only those platforms.
8591
// SAFETY: Only accessible from within this module.
86-
static HANDLER_ADDRESS: AtomicPtr<()> = AtomicPtr::new(core::ptr::null_mut());
92+
static HANDLER: AtomicPtr<()> = AtomicPtr::new(panic as *mut ());
8793

8894
/// Set the global error handler.
8995
///
@@ -116,35 +122,33 @@ mod global_error_handler {
116122
/// [before]: https://doc.rust-lang.org/nightly/core/sync/atomic/index.html#memory-model-for-atomic-accesses
117123
/// [`default_error_handler`]: super::default_error_handler
118124
pub fn set_global_default_error_handler(handler: fn(BevyError, ErrorContext)) {
119-
if HANDLER_ADDRESS
120-
.compare_exchange(core::ptr::null_mut(), handler as *mut (), AcqRel, Acquire)
125+
// Prevent the handler from being set multiple times.
126+
// We use a seperate atomic instead of trying `compare_exchange` on `HANDLER_ADDRESS`
127+
// because Rust doesn't guarantee that function addresses are unique.
128+
static INITIALIZED: AtomicBool = AtomicBool::new(false);
129+
if INITIALIZED
130+
.compare_exchange(false, true, AcqRel, Acquire)
121131
.is_err()
122132
{
123133
panic!("Global error handler set multiple times");
124134
}
135+
HANDLER.store(handler as *mut (), Relaxed);
125136
}
126137

138+
/// The default error handler. This defaults to [`panic`],
139+
/// but you can override this behavior via [`set_global_default_error_handler`].
127140
#[inline]
128-
pub(super) fn get() -> Option<fn(BevyError, ErrorContext)> {
141+
pub fn default_error_handler() -> fn(BevyError, ErrorContext) {
129142
// The error handler must have been already set from the perspective of this thread,
130143
// otherwise we will panic. It will never be updated after this point.
131144
// We therefore only need a relaxed load.
132-
let ptr = HANDLER_ADDRESS.load(Relaxed);
133-
// SAFETY: We only ever store null or a valid handler function.
134-
// `null` is guaranteed to be represent `None`:
135-
// https://rust-lang.github.io/unsafe-code-guidelines/layout/function-pointers.html#representation
145+
let ptr = HANDLER.load(Relaxed);
146+
// SAFETY: We only ever store valid handler functions.
136147
unsafe { core::mem::transmute(ptr) }
137148
}
138149
}
139150

140-
pub use global_error_handler::set_global_default_error_handler;
141-
142-
/// The default error handler. This defaults to [`panic()`],
143-
/// but you can override this behavior via [`set_global_error_handler`].
144-
#[inline]
145-
pub fn default_error_handler() -> fn(BevyError, ErrorContext) {
146-
global_error_handler::get().unwrap_or(panic)
147-
}
151+
pub use global_error_handler::{default_error_handler, set_global_default_error_handler};
148152

149153
macro_rules! inner {
150154
($call:path, $e:ident, $c:ident) => {

crates/bevy_ecs/src/error/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//! [`panic`] error handler function is used, resulting in a panic with the error message attached.
99
//!
1010
//! You can change the default behavior by registering a custom error handler:
11-
//! Use [`set_global_default_error_handler`](crate::error::set_global_default_error_handler)
11+
//! Use [`set_global_default_error_handler`]
1212
//! to set a custom error handler function for your entire app.
1313
//! In practice, this is generally feature-flagged: panicking or loudly logging errors in development,
1414
//! and quietly logging or ignoring them in production to avoid crashing the app.

0 commit comments

Comments
 (0)