@@ -76,14 +76,20 @@ impl ErrorContext {
76
76
}
77
77
78
78
mod global_error_handler {
79
- use super :: { BevyError , ErrorContext } ;
79
+ use super :: { panic , BevyError , ErrorContext } ;
80
80
use bevy_platform_support:: sync:: atomic:: {
81
- AtomicPtr ,
81
+ AtomicBool , AtomicPtr ,
82
82
Ordering :: { AcqRel , Acquire , Relaxed } ,
83
83
} ;
84
84
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.
85
91
// 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 ( ) ) ;
87
93
88
94
/// Set the global error handler.
89
95
///
@@ -116,35 +122,33 @@ mod global_error_handler {
116
122
/// [before]: https://doc.rust-lang.org/nightly/core/sync/atomic/index.html#memory-model-for-atomic-accesses
117
123
/// [`default_error_handler`]: super::default_error_handler
118
124
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 )
121
131
. is_err ( )
122
132
{
123
133
panic ! ( "Global error handler set multiple times" ) ;
124
134
}
135
+ HANDLER . store ( handler as * mut ( ) , Relaxed ) ;
125
136
}
126
137
138
+ /// The default error handler. This defaults to [`panic`],
139
+ /// but you can override this behavior via [`set_global_default_error_handler`].
127
140
#[ inline]
128
- pub ( super ) fn get ( ) -> Option < fn ( BevyError , ErrorContext ) > {
141
+ pub fn default_error_handler ( ) -> fn ( BevyError , ErrorContext ) {
129
142
// The error handler must have been already set from the perspective of this thread,
130
143
// otherwise we will panic. It will never be updated after this point.
131
144
// 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.
136
147
unsafe { core:: mem:: transmute ( ptr) }
137
148
}
138
149
}
139
150
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} ;
148
152
149
153
macro_rules! inner {
150
154
( $call: path, $e: ident, $c: ident) => {
0 commit comments