Skip to content

Commit ec9df9f

Browse files
remove configurable_error_handler feature
1 parent 45ba5b9 commit ec9df9f

File tree

9 files changed

+89
-84
lines changed

9 files changed

+89
-84
lines changed

Cargo.toml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,6 @@ bevy_log = ["bevy_internal/bevy_log"]
291291
# Enable input focus subsystem
292292
bevy_input_focus = ["bevy_internal/bevy_input_focus"]
293293

294-
# Use the configurable global error handler as the default error handler.
295-
configurable_error_handler = ["bevy_internal/configurable_error_handler"]
296-
297294
# Enable passthrough loading for SPIR-V shaders (Only supported on Vulkan, shader capabilities and extensions must agree with the platform implementation)
298295
spirv_shader_passthrough = ["bevy_internal/spirv_shader_passthrough"]
299296

@@ -2215,7 +2212,6 @@ wasm = false
22152212
name = "fallible_params"
22162213
path = "examples/ecs/fallible_params.rs"
22172214
doc-scrape-examples = true
2218-
required-features = ["configurable_error_handler"]
22192215

22202216
[package.metadata.example.fallible_params]
22212217
name = "Fallible System Parameters"
@@ -2227,7 +2223,7 @@ wasm = false
22272223
name = "error_handling"
22282224
path = "examples/ecs/error_handling.rs"
22292225
doc-scrape-examples = true
2230-
required-features = ["bevy_mesh_picking_backend", "configurable_error_handler"]
2226+
required-features = ["bevy_mesh_picking_backend"]
22312227

22322228
[package.metadata.example.error_handling]
22332229
name = "Error handling"

crates/bevy_ecs/Cargo.toml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,6 @@ bevy_reflect = ["dep:bevy_reflect"]
3333
## Extends reflection support to functions.
3434
reflect_functions = ["bevy_reflect", "bevy_reflect/functions"]
3535

36-
## Use the configurable global error handler as the default error handler.
37-
##
38-
## This is typically used to turn panics from the ECS into loggable errors.
39-
## This may be useful for production builds,
40-
## but can result in a measurable performance impact, especially for commands.
41-
configurable_error_handler = []
42-
4336
## Enables automatic backtrace capturing in BevyError
4437
backtrace = ["std"]
4538

crates/bevy_ecs/src/error/handler.rs

Lines changed: 74 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#[cfg(feature = "configurable_error_handler")]
2-
use bevy_platform::sync::OnceLock;
31
use core::fmt::Display;
42

53
use crate::{component::Tick, error::BevyError};
@@ -77,53 +75,83 @@ impl ErrorContext {
7775
}
7876
}
7977

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+
}
122137

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+
}
125151
}
126152

153+
pub use global_error_handler::{default_error_handler, set_global_default_error_handler};
154+
127155
macro_rules! inner {
128156
($call:path, $e:ident, $c:ident) => {
129157
$call!(

crates/bevy_ecs/src/error/mod.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
//! All [`BevyError`]s returned by a system, observer or command are handled by an "error handler". By default, the
88
//! [`panic`] error handler function is used, resulting in a panic with the error message attached.
99
//!
10-
//! You can change the default behavior by registering a custom error handler.
11-
//! Modify the [`GLOBAL_ERROR_HANDLER`] value to set a custom error handler function for your entire app.
10+
//! You can change the default behavior by registering a custom error handler:
11+
//! Use [`set_global_default_error_handler`]
12+
//! to set a custom error handler function for your entire app.
1213
//! In practice, this is generally feature-flagged: panicking or loudly logging errors in development,
1314
//! and quietly logging or ignoring them in production to avoid crashing the app.
1415
//!
@@ -33,10 +34,8 @@
3334
//! The [`ErrorContext`] allows you to access additional details relevant to providing
3435
//! context surrounding the error – such as the system's [`name`] – in your error messages.
3536
//!
36-
//! Remember to turn on the `configurable_error_handler` feature to set a global error handler!
37-
//!
3837
//! ```rust, ignore
39-
//! use bevy_ecs::error::{GLOBAL_ERROR_HANDLER, BevyError, ErrorContext};
38+
//! use bevy_ecs::error::{set_global_default_error_handler, BevyError, ErrorContext};
4039
//! use log::trace;
4140
//!
4241
//! fn my_error_handler(error: BevyError, ctx: ErrorContext) {
@@ -48,8 +47,7 @@
4847
//! }
4948
//!
5049
//! fn main() {
51-
//! // This requires the "configurable_error_handler" feature to be enabled to be in scope.
52-
//! GLOBAL_ERROR_HANDLER.set(my_error_handler).expect("The error handler can only be set once.");
50+
//! set_global_default_error_handler(my_error_handler);
5351
//!
5452
//! // Initialize your Bevy App here
5553
//! }

crates/bevy_ecs/src/system/commands/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ use crate::{
8989
/// which will be passed to an [error handler](crate::error) if the `Result` is an error.
9090
///
9191
/// The [default error handler](crate::error::default_error_handler) panics.
92-
/// It can be configured by setting the `GLOBAL_ERROR_HANDLER`.
92+
/// It can be configured via [`set_global_default_error_handler`](crate::error::set_global_default_error_handler).
9393
///
9494
/// Alternatively, you can customize the error handler for a specific command
9595
/// by calling [`Commands::queue_handled`].
@@ -1176,7 +1176,7 @@ impl<'w, 's> Commands<'w, 's> {
11761176
/// which will be passed to an [error handler](crate::error) if the `Result` is an error.
11771177
///
11781178
/// The [default error handler](crate::error::default_error_handler) panics.
1179-
/// It can be configured by setting the `GLOBAL_ERROR_HANDLER`.
1179+
/// It can be configured via [`set_global_default_error_handler`](crate::error::set_global_default_error_handler).
11801180
///
11811181
/// Alternatively, you can customize the error handler for a specific command
11821182
/// by calling [`EntityCommands::queue_handled`].

crates/bevy_internal/Cargo.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,6 @@ custom_cursor = ["bevy_winit/custom_cursor"]
279279
# Experimental support for nodes that are ignored for UI layouting
280280
ghost_nodes = ["bevy_ui/ghost_nodes"]
281281

282-
# Use the configurable global error handler as the default error handler.
283-
configurable_error_handler = ["bevy_ecs/configurable_error_handler"]
284-
285282
# Allows access to the `std` crate. Enabling this feature will prevent compilation
286283
# on `no_std` targets, but provides access to certain additional features on
287284
# supported platforms.

docs/cargo_features.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ The default feature set enables most of the expected features of a game engine,
7070
|bevy_remote|Enable the Bevy Remote Protocol|
7171
|bevy_ui_debug|Provides a debug overlay for bevy UI|
7272
|bmp|BMP image format support|
73-
|configurable_error_handler|Use the configurable global error handler as the default error handler.|
7473
|critical-section|`critical-section` provides the building blocks for synchronization primitives on all platforms, including `no_std`.|
7574
|dds|DDS compressed texture support|
7675
|debug_glam_assert|Enable assertions in debug builds to check the validity of parameters passed to glam|

examples/ecs/error_handling.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
//! Showcases how fallible systems and observers can make use of Rust's powerful result handling
22
//! syntax.
3-
//!
4-
//! Important note: to set the global error handler, the `configurable_error_handler` feature must be
5-
//! enabled. This feature is disabled by default, as it may introduce runtime overhead, especially for commands.
63
74
use bevy::ecs::{
8-
error::{warn, GLOBAL_ERROR_HANDLER},
5+
error::{set_global_default_error_handler, warn},
96
world::DeferredWorld,
107
};
118
use bevy::math::sampling::UniformMeshSampler;
@@ -22,9 +19,7 @@ fn main() {
2219
// Here we set the global error handler using one of the built-in
2320
// error handlers. Bevy provides built-in handlers for `panic`, `error`, `warn`, `info`,
2421
// `debug`, `trace` and `ignore`.
25-
GLOBAL_ERROR_HANDLER
26-
.set(warn)
27-
.expect("The error handler can only be set once, globally.");
22+
set_global_default_error_handler(warn);
2823

2924
let mut app = App::new();
3025

examples/ecs/fallible_params.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! from running if their acquiry conditions aren't met.
33
//!
44
//! Fallible system parameters include:
5-
//! - [`Res<R>`], [`ResMut<R>`] - Resource has to exist, and the [`GLOBAL_ERROR_HANDLER`] will be called if it doesn't.
5+
//! - [`Res<R>`], [`ResMut<R>`] - Resource has to exist, and the [`default_error_handler`] will be called if it doesn't.
66
//! - [`Single<D, F>`] - There must be exactly one matching entity, but the system will be silently skipped otherwise.
77
//! - [`Option<Single<D, F>>`] - There must be zero or one matching entity. The system will be silently skipped if there are more.
88
//! - [`Populated<D, F>`] - There must be at least one matching entity, but the system will be silently skipped otherwise.
@@ -18,18 +18,17 @@
1818
//!
1919
//! [`SystemParamValidationError`]: bevy::ecs::system::SystemParamValidationError
2020
//! [`SystemParam::validate_param`]: bevy::ecs::system::SystemParam::validate_param
21+
//! [`default_error_handler`]: bevy::ecs::error::default_error_handler
2122
22-
use bevy::ecs::error::{warn, GLOBAL_ERROR_HANDLER};
23+
use bevy::ecs::error::{set_global_default_error_handler, warn};
2324
use bevy::prelude::*;
2425
use rand::Rng;
2526

2627
fn main() {
2728
// By default, if a parameter fail to be fetched,
28-
// the `GLOBAL_ERROR_HANDLER` will be used to handle the error,
29+
// the `default_error_handler` will be used to handle the error,
2930
// which by default is set to panic.
30-
GLOBAL_ERROR_HANDLER
31-
.set(warn)
32-
.expect("The error handler can only be set once, globally.");
31+
set_global_default_error_handler(warn);
3332

3433
println!();
3534
println!("Press 'A' to add enemy ships and 'R' to remove them.");

0 commit comments

Comments
 (0)