|
1 |
| -#[cfg(feature = "configurable_error_handler")] |
2 |
| -use bevy_platform::sync::OnceLock; |
3 | 1 | use core::fmt::Display;
|
4 | 2 |
|
5 | 3 | use crate::{component::Tick, error::BevyError};
|
@@ -77,53 +75,83 @@ impl ErrorContext {
|
77 | 75 | }
|
78 | 76 | }
|
79 | 77 |
|
80 |
| -/// A global error handler. This can be set at startup, as long as it is set before |
81 |
| -/// any uses. This should generally be configured _before_ initializing the app. |
82 |
| -/// |
83 |
| -/// This should be set inside of your `main` function, before initializing the Bevy app. |
84 |
| -/// The value of this error handler can be accessed using the [`default_error_handler`] function, |
85 |
| -/// which calls [`OnceLock::get_or_init`] to get the value. |
86 |
| -/// |
87 |
| -/// **Note:** this is only available when the `configurable_error_handler` feature of `bevy_ecs` (or `bevy`) is enabled! |
88 |
| -/// |
89 |
| -/// # Example |
90 |
| -/// |
91 |
| -/// ``` |
92 |
| -/// # use bevy_ecs::error::{GLOBAL_ERROR_HANDLER, warn}; |
93 |
| -/// GLOBAL_ERROR_HANDLER.set(warn).expect("The error handler can only be set once, globally."); |
94 |
| -/// // initialize Bevy App here |
95 |
| -/// ``` |
96 |
| -/// |
97 |
| -/// To use this error handler in your app for custom error handling logic: |
98 |
| -/// |
99 |
| -/// ```rust |
100 |
| -/// use bevy_ecs::error::{default_error_handler, GLOBAL_ERROR_HANDLER, BevyError, ErrorContext, panic}; |
101 |
| -/// |
102 |
| -/// fn handle_errors(error: BevyError, ctx: ErrorContext) { |
103 |
| -/// let error_handler = default_error_handler(); |
104 |
| -/// error_handler(error, ctx); |
105 |
| -/// } |
106 |
| -/// ``` |
107 |
| -/// |
108 |
| -/// # Warning |
109 |
| -/// |
110 |
| -/// As this can *never* be overwritten, library code should never set this value. |
111 |
| -#[cfg(feature = "configurable_error_handler")] |
112 |
| -pub static GLOBAL_ERROR_HANDLER: OnceLock<fn(BevyError, ErrorContext)> = OnceLock::new(); |
113 |
| - |
114 |
| -/// The default error handler. This defaults to [`panic()`], |
115 |
| -/// but if set, the [`GLOBAL_ERROR_HANDLER`] will be used instead, enabling error handler customization. |
116 |
| -/// The `configurable_error_handler` feature must be enabled to change this from the panicking default behavior, |
117 |
| -/// as there may be runtime overhead. |
118 |
| -#[inline] |
119 |
| -pub fn default_error_handler() -> fn(BevyError, ErrorContext) { |
120 |
| - #[cfg(not(feature = "configurable_error_handler"))] |
121 |
| - return panic; |
| 78 | +mod global_error_handler { |
| 79 | + use super::{panic, BevyError, ErrorContext}; |
| 80 | + use bevy_platform_support::sync::atomic::{ |
| 81 | + AtomicBool, AtomicPtr, |
| 82 | + Ordering::{AcqRel, Acquire, Relaxed}, |
| 83 | + }; |
| 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 `OnceLock` |
| 90 | + /// on only those platforms. |
| 91 | + /// SAFETY: Only accessible from within this module. |
| 92 | + static HANDLER: AtomicPtr<()> = AtomicPtr::new(panic as *mut ()); |
| 93 | + |
| 94 | + /// Set the global error handler. |
| 95 | + /// |
| 96 | + /// If used, this should be called [before] any uses of [`default_error_handler`], |
| 97 | + /// generally inside your `main` function before initializing the app. |
| 98 | + /// |
| 99 | + /// # Example |
| 100 | + /// |
| 101 | + /// ``` |
| 102 | + /// # use bevy_ecs::error::{set_global_default_error_handler, warn}; |
| 103 | + /// set_global_default_error_handler(warn); |
| 104 | + /// // initialize Bevy App here |
| 105 | + /// ``` |
| 106 | + /// |
| 107 | + /// To use this error handler in your app for custom error handling logic: |
| 108 | + /// |
| 109 | + /// ```rust |
| 110 | + /// use bevy_ecs::error::{default_error_handler, BevyError, ErrorContext}; |
| 111 | + /// |
| 112 | + /// fn handle_errors(error: BevyError, ctx: ErrorContext) { |
| 113 | + /// let error_handler = default_error_handler(); |
| 114 | + /// error_handler(error, ctx); |
| 115 | + /// } |
| 116 | + /// ``` |
| 117 | + /// |
| 118 | + /// # Warning |
| 119 | + /// |
| 120 | + /// As this can *never* be overwritten, library code should never set this value. |
| 121 | + /// |
| 122 | + /// [before]: https://doc.rust-lang.org/nightly/core/sync/atomic/index.html#memory-model-for-atomic-accesses |
| 123 | + /// [`default_error_handler`]: super::default_error_handler |
| 124 | + pub fn set_global_default_error_handler(handler: fn(BevyError, ErrorContext)) { |
| 125 | + // Prevent the handler from being set multiple times. |
| 126 | + // We use a separate 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) |
| 131 | + .is_err() |
| 132 | + { |
| 133 | + panic!("Global error handler set multiple times"); |
| 134 | + } |
| 135 | + HANDLER.store(handler as *mut (), Relaxed); |
| 136 | + } |
122 | 137 |
|
123 |
| - #[cfg(feature = "configurable_error_handler")] |
124 |
| - return *GLOBAL_ERROR_HANDLER.get_or_init(|| panic); |
| 138 | + /// The default error handler. This defaults to [`panic`], |
| 139 | + /// but you can override this behavior via [`set_global_default_error_handler`]. |
| 140 | + /// |
| 141 | + /// [`panic`]: super::panic |
| 142 | + #[inline] |
| 143 | + pub fn default_error_handler() -> fn(BevyError, ErrorContext) { |
| 144 | + // The error handler must have been already set from the perspective of this thread, |
| 145 | + // otherwise we will panic. It will never be updated after this point. |
| 146 | + // We therefore only need a relaxed load. |
| 147 | + let ptr = HANDLER.load(Relaxed); |
| 148 | + // SAFETY: We only ever store valid handler functions. |
| 149 | + unsafe { core::mem::transmute(ptr) } |
| 150 | + } |
125 | 151 | }
|
126 | 152 |
|
| 153 | +pub use global_error_handler::{default_error_handler, set_global_default_error_handler}; |
| 154 | + |
127 | 155 | macro_rules! inner {
|
128 | 156 | ($call:path, $e:ident, $c:ident) => {
|
129 | 157 | $call!(
|
|
0 commit comments