Skip to content

Commit e7e9e61

Browse files
move default error handler to World
1 parent b3f8707 commit e7e9e61

File tree

12 files changed

+97
-121
lines changed

12 files changed

+97
-121
lines changed

crates/bevy_app/src/app.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use alloc::{
1010
pub use bevy_derive::AppLabel;
1111
use bevy_ecs::{
1212
component::RequiredComponentsError,
13+
error::{panic, ErrorHandler},
1314
event::{event_update_system, EventCursor},
1415
intern::Interned,
1516
prelude::*,
@@ -85,6 +86,7 @@ pub struct App {
8586
/// [`WinitPlugin`]: https://docs.rs/bevy/latest/bevy/winit/struct.WinitPlugin.html
8687
/// [`ScheduleRunnerPlugin`]: https://docs.rs/bevy/latest/bevy/app/struct.ScheduleRunnerPlugin.html
8788
pub(crate) runner: RunnerFn,
89+
default_error_handler: ErrorHandler,
8890
}
8991

9092
impl Debug for App {
@@ -143,6 +145,7 @@ impl App {
143145
sub_apps: HashMap::default(),
144146
},
145147
runner: Box::new(run_once),
148+
default_error_handler: panic,
146149
}
147150
}
148151

@@ -1334,6 +1337,34 @@ impl App {
13341337
self.world_mut().add_observer(observer);
13351338
self
13361339
}
1340+
1341+
/// Gets the error handler to set for new supapps.
1342+
///
1343+
/// Note that the error handler of existing subapps may differ.
1344+
pub fn get_error_handler(&self) -> ErrorHandler {
1345+
self.default_error_handler
1346+
}
1347+
1348+
/// Override the error handler for the all subapps (including the main one).
1349+
///
1350+
/// This handler will be called when an error is produced and not otherwise handled.
1351+
///
1352+
/// # Example
1353+
/// ```
1354+
/// # use bevy_app::*;
1355+
/// # use bevy_ecs::error::warn;
1356+
/// App::new()
1357+
/// .set_error_handler(warn)
1358+
/// .add_plugins(DefaultPlugins)
1359+
/// .run()
1360+
/// ```
1361+
pub fn set_error_handler(&mut self, handler: ErrorHandler) -> &mut Self {
1362+
self.default_error_handler = handler;
1363+
for sub_app in self.sub_apps.iter_mut() {
1364+
sub_app.world_mut().set_default_error_handler(handler);
1365+
}
1366+
self
1367+
}
13371368
}
13381369

13391370
type RunnerFn = Box<dyn FnOnce(App) -> AppExit>;

crates/bevy_ecs/src/error/command_handling.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,24 @@ use crate::{
66
world::{error::EntityMutableFetchError, World},
77
};
88

9-
use super::{default_error_handler, BevyError, ErrorContext};
9+
use super::{BevyError, ErrorContext, ErrorHandler};
1010

1111
/// Takes a [`Command`] that returns a Result and uses a given error handler function to convert it into
1212
/// a [`Command`] that internally handles an error if it occurs and returns `()`.
13-
pub trait HandleError<Out = ()> {
13+
pub trait HandleError<Out = ()>: Send + 'static {
1414
/// Takes a [`Command`] that returns a Result and uses a given error handler function to convert it into
1515
/// a [`Command`] that internally handles an error if it occurs and returns `()`.
16-
fn handle_error_with(self, error_handler: fn(BevyError, ErrorContext)) -> impl Command;
16+
fn handle_error_with(self, error_handler: ErrorHandler) -> impl Command;
1717
/// Takes a [`Command`] that returns a Result and uses the default error handler function to convert it into
1818
/// a [`Command`] that internally handles an error if it occurs and returns `()`.
1919
fn handle_error(self) -> impl Command
2020
where
2121
Self: Sized,
2222
{
23-
self.handle_error_with(default_error_handler())
23+
move |world: &mut World| {
24+
self.handle_error_with(world.default_error_handler)
25+
.apply(world);
26+
}
2427
}
2528
}
2629

@@ -29,7 +32,7 @@ where
2932
C: Command<Result<T, E>>,
3033
E: Into<BevyError>,
3134
{
32-
fn handle_error_with(self, error_handler: fn(BevyError, ErrorContext)) -> impl Command {
35+
fn handle_error_with(self, error_handler: ErrorHandler) -> impl Command {
3336
move |world: &mut World| match self.apply(world) {
3437
Ok(_) => {}
3538
Err(err) => (error_handler)(

crates/bevy_ecs/src/error/handler.rs

Lines changed: 3 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -75,83 +75,6 @@ impl ErrorContext {
7575
}
7676
}
7777

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-
}
137-
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-
}
151-
}
152-
153-
pub use global_error_handler::{default_error_handler, set_global_default_error_handler};
154-
15578
macro_rules! inner {
15679
($call:path, $e:ident, $c:ident) => {
15780
$call!(
@@ -163,6 +86,9 @@ macro_rules! inner {
16386
};
16487
}
16588

89+
/// Defines how Bevy reacts to errors.
90+
pub type ErrorHandler = fn(BevyError, ErrorContext);
91+
16692
/// Error handler that panics with the system error.
16793
#[track_caller]
16894
#[inline]

crates/bevy_ecs/src/observer/runner.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use core::any::Any;
33

44
use crate::{
55
component::{ComponentHook, ComponentId, HookContext, Mutable, StorageType},
6-
error::{default_error_handler, ErrorContext},
6+
error::ErrorContext,
77
observer::{ObserverDescriptor, ObserverTrigger},
88
prelude::*,
99
query::DebugCheckedUnwrap,
@@ -458,14 +458,14 @@ fn hook_on_add<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
458458
..Default::default()
459459
};
460460

461-
let error_handler = default_error_handler();
461+
let default_error_handler = world.default_error_handler;
462462

463463
// Initialize System
464464
let system: *mut dyn ObserverSystem<E, B> =
465465
if let Some(mut observe) = world.get_mut::<Observer>(entity) {
466466
descriptor.merge(&observe.descriptor);
467467
if observe.error_handler.is_none() {
468-
observe.error_handler = Some(error_handler);
468+
observe.error_handler = Some(default_error_handler);
469469
}
470470
let system = observe.system.downcast_mut::<S>().unwrap();
471471
&mut *system

crates/bevy_ecs/src/schedule/executor/multi_threaded.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use tracing::{info_span, Span};
1414

1515
use crate::{
1616
archetype::ArchetypeComponentId,
17-
error::{default_error_handler, BevyError, ErrorContext, Result},
17+
error::{ErrorContext, ErrorHandler, Result},
1818
prelude::Resource,
1919
query::Access,
2020
schedule::{is_apply_deferred, BoxedCondition, ExecutorKind, SystemExecutor, SystemSchedule},
@@ -131,7 +131,7 @@ pub struct ExecutorState {
131131
struct Context<'scope, 'env, 'sys> {
132132
environment: &'env Environment<'env, 'sys>,
133133
scope: &'scope Scope<'scope, 'env, ()>,
134-
error_handler: fn(BevyError, ErrorContext),
134+
error_handler: ErrorHandler,
135135
}
136136

137137
impl Default for MultiThreadedExecutor {
@@ -182,7 +182,7 @@ impl SystemExecutor for MultiThreadedExecutor {
182182
schedule: &mut SystemSchedule,
183183
world: &mut World,
184184
_skip_systems: Option<&FixedBitSet>,
185-
error_handler: fn(BevyError, ErrorContext),
185+
error_handler: ErrorHandler,
186186
) {
187187
let state = self.state.get_mut().unwrap();
188188
// reset counts
@@ -538,7 +538,6 @@ impl ExecutorState {
538538
world: UnsafeWorldCell,
539539
) -> bool {
540540
let mut should_run = !self.skipped_systems.contains(system_index);
541-
let error_handler = default_error_handler();
542541

543542
for set_idx in conditions.sets_with_conditions_of_systems[system_index].ones() {
544543
if self.evaluated_sets.contains(set_idx) {
@@ -587,7 +586,7 @@ impl ExecutorState {
587586
Ok(()) => true,
588587
Err(e) => {
589588
if !e.skipped {
590-
error_handler(
589+
world.default_error_handler()(
591590
e.into(),
592591
ErrorContext::System {
593592
name: system.name(),
@@ -787,8 +786,6 @@ unsafe fn evaluate_and_fold_conditions(
787786
conditions: &mut [BoxedCondition],
788787
world: UnsafeWorldCell,
789788
) -> bool {
790-
let error_handler = default_error_handler();
791-
792789
#[expect(
793790
clippy::unnecessary_fold,
794791
reason = "Short-circuiting here would prevent conditions from mutating their own state as needed."
@@ -804,7 +801,7 @@ unsafe fn evaluate_and_fold_conditions(
804801
Ok(()) => (),
805802
Err(e) => {
806803
if !e.skipped {
807-
error_handler(
804+
world.default_error_handler()(
808805
e.into(),
809806
ErrorContext::System {
810807
name: condition.name(),

crates/bevy_ecs/src/schedule/executor/simple.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use tracing::info_span;
88
use std::eprintln;
99

1010
use crate::{
11-
error::{default_error_handler, BevyError, ErrorContext},
11+
error::{ErrorContext, ErrorHandler},
1212
schedule::{
1313
executor::is_apply_deferred, BoxedCondition, ExecutorKind, SystemExecutor, SystemSchedule,
1414
},
@@ -44,7 +44,7 @@ impl SystemExecutor for SimpleExecutor {
4444
schedule: &mut SystemSchedule,
4545
world: &mut World,
4646
_skip_systems: Option<&FixedBitSet>,
47-
error_handler: fn(BevyError, ErrorContext),
47+
error_handler: ErrorHandler,
4848
) {
4949
// If stepping is enabled, make sure we skip those systems that should
5050
// not be run.
@@ -167,8 +167,6 @@ impl SimpleExecutor {
167167
}
168168

169169
fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &mut World) -> bool {
170-
let error_handler = default_error_handler();
171-
172170
#[expect(
173171
clippy::unnecessary_fold,
174172
reason = "Short-circuiting here would prevent conditions from mutating their own state as needed."
@@ -180,7 +178,7 @@ fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &mut W
180178
Ok(()) => (),
181179
Err(e) => {
182180
if !e.skipped {
183-
error_handler(
181+
world.default_error_handler()(
184182
e.into(),
185183
ErrorContext::System {
186184
name: condition.name(),

crates/bevy_ecs/src/schedule/executor/single_threaded.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use tracing::info_span;
88
use std::eprintln;
99

1010
use crate::{
11-
error::{default_error_handler, BevyError, ErrorContext},
11+
error::{ErrorContext, ErrorHandler},
1212
schedule::{is_apply_deferred, BoxedCondition, ExecutorKind, SystemExecutor, SystemSchedule},
1313
world::World,
1414
};
@@ -50,7 +50,7 @@ impl SystemExecutor for SingleThreadedExecutor {
5050
schedule: &mut SystemSchedule,
5151
world: &mut World,
5252
_skip_systems: Option<&FixedBitSet>,
53-
error_handler: fn(BevyError, ErrorContext),
53+
error_handler: ErrorHandler,
5454
) {
5555
// If stepping is enabled, make sure we skip those systems that should
5656
// not be run.
@@ -211,8 +211,6 @@ impl SingleThreadedExecutor {
211211
}
212212

213213
fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &mut World) -> bool {
214-
let error_handler: fn(BevyError, ErrorContext) = default_error_handler();
215-
216214
#[expect(
217215
clippy::unnecessary_fold,
218216
reason = "Short-circuiting here would prevent conditions from mutating their own state as needed."
@@ -224,7 +222,7 @@ fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &mut W
224222
Ok(()) => (),
225223
Err(e) => {
226224
if !e.skipped {
227-
error_handler(
225+
world.default_error_handler()(
228226
e.into(),
229227
ErrorContext::System {
230228
name: condition.name(),

crates/bevy_ecs/src/schedule/schedule.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ use tracing::info_span;
2727

2828
use crate::{
2929
component::{ComponentId, Components, Tick},
30-
error::default_error_handler,
3130
prelude::Component,
3231
resource::Resource,
3332
schedule::*,
@@ -441,7 +440,7 @@ impl Schedule {
441440
self.initialize(world)
442441
.unwrap_or_else(|e| panic!("Error when initializing schedule {:?}: {e}", self.label));
443442

444-
let error_handler = default_error_handler();
443+
let error_handler = world.default_error_handler();
445444

446445
#[cfg(not(feature = "bevy_debug_stepping"))]
447446
self.executor

0 commit comments

Comments
 (0)