Skip to main content

bevy_ecs/observer/
runner.rs

1//! Logic for evaluating observers, and storing functions inside of observers.
2
3use core::any::Any;
4
5use crate::{
6    error::ErrorContext,
7    event::Event,
8    observer::TriggerContext,
9    prelude::*,
10    query::DebugCheckedUnwrap,
11    system::{ObserverSystem, RunSystemError},
12    world::DeferredWorld,
13};
14use bevy_ptr::PtrMut;
15
16/// Type for function that is run when an observer is triggered.
17///
18/// Typically refers to the default runner that runs the system stored in the associated [`Observer`] component,
19/// but can be overridden for custom behavior.
20///
21/// See `observer_system_runner` for safety considerations.
22pub type ObserverRunner =
23    unsafe fn(DeferredWorld, observer: Entity, &TriggerContext, event: PtrMut, trigger: PtrMut);
24
25/// # Safety
26///
27/// - `world` must be the [`DeferredWorld`] that the `entity` is defined in
28/// - `event_ptr` must match the `E` [`Event`] type.
29/// - `trigger_ptr` must match the [`Event::Trigger`] type for `E`.
30/// - `trigger_context`'s [`TriggerContext::event_key`] must match the `E` event type.
31///
32// NOTE: The way `Trigger` and `On` interact in this implementation is _subtle_ and _easily invalidated_
33// from a soundness perspective. Please read and understand the safety comments before making any changes,
34// either here or in `On`.
35pub(super) unsafe fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
36    mut world: DeferredWorld,
37    observer: Entity,
38    trigger_context: &TriggerContext,
39    event_ptr: PtrMut,
40    trigger_ptr: PtrMut,
41) {
42    let world = world.as_unsafe_world_cell();
43
44    // SAFETY: Observer was triggered so must still exist in world
45    let observer_cell = unsafe { world.get_entity(observer).debug_checked_unwrap() };
46    // SAFETY: Observer was triggered so must have an `Observer`
47    let mut state = unsafe { observer_cell.get_mut::<Observer>().debug_checked_unwrap() };
48
49    // TODO: Move this check into the observer cache to avoid dynamic dispatch
50    let last_trigger = world.last_trigger_id();
51    if state.last_trigger_id == last_trigger {
52        return;
53    }
54    state.last_trigger_id = last_trigger;
55
56    // SAFETY:
57    // - Conditions are initialized during observer registration (hook_on_add)
58    // - Conditions are ReadOnlySystem (enforced by SystemCondition trait)
59    // - No aliasing: we hold &mut Observer, but conditions only read world state
60    let mut should_run = true;
61    for condition in state.conditions.iter_mut() {
62        // SAFETY: See the safety comment above.
63        should_run &= unsafe { condition.check(world) };
64    }
65
66    if !should_run {
67        return;
68    }
69
70    // SAFETY: Caller ensures `trigger_ptr` is castable to `&mut E::Trigger<'_>`
71    // The soundness story here is complicated: This casts to &'a mut E::Trigger<'a> which notably
72    // casts the _arbitrary lifetimes_ of the passed in `trigger_ptr` (&'w E::Trigger<'t>, which are
73    // 'w and 't on On<'w, 't>) as the _same_ lifetime 'a, which is _local to this function call_.
74    // This becomes On<'a, 'a> in practice. This is why `On<'w, 't>` has the strict constraint that
75    // the 'w lifetime can never be exposed. To do so would make it possible to introduce use-after-free bugs.
76    // See this thread for more details: <https://github.com/bevyengine/bevy/pull/20731#discussion_r2311907935>
77    let trigger: &mut E::Trigger<'_> = unsafe { trigger_ptr.deref_mut() };
78
79    let on: On<E, B> = On::new(
80        // SAFETY: Caller ensures `ptr` is castable to `&mut E`
81        unsafe { event_ptr.deref_mut() },
82        observer,
83        trigger,
84        trigger_context,
85    );
86
87    // SAFETY:
88    // - observer was triggered so must have an `Observer` component.
89    // - observer cannot be dropped or mutated until after the system pointer is already dropped.
90    let system: *mut dyn ObserverSystem<E, B> = unsafe {
91        let system: &mut dyn Any = state.system.as_mut();
92        let system = system.downcast_mut::<S>().debug_checked_unwrap();
93        &mut *system
94    };
95
96    // SAFETY:
97    // - there are no outstanding references to world except a private component
98    // - system is an `ObserverSystem` so won't mutate world beyond the access of a `DeferredWorld`
99    //   and is never exclusive
100    // - system is the same type erased system from above
101    unsafe {
102        #[cfg(feature = "hotpatching")]
103        if world
104            .get_resource_ref::<crate::HotPatchChanges>()
105            .is_none_or(|r| r.is_changed_after((*system).get_last_run()))
106        {
107            (*system).refresh_hotpatch();
108        };
109
110        if let Err(RunSystemError::Failed(err)) = (*system).run_unsafe(on, world) {
111            let handler = state
112                .error_handler
113                .unwrap_or_else(|| world.fallback_error_handler());
114            handler(
115                err,
116                ErrorContext::Observer {
117                    name: (*system).name(),
118                    last_run: (*system).get_last_run(),
119                },
120            );
121        };
122        (*system).queue_deferred(world.into_deferred());
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129    use crate::{
130        error::{ignore, FallbackErrorHandler},
131        event::Event,
132        observer::On,
133    };
134
135    #[derive(Event)]
136    struct TriggerEvent;
137
138    #[test]
139    #[should_panic(expected = "I failed!")]
140    fn test_fallible_observer() {
141        fn system(_: On<TriggerEvent>) -> Result {
142            Err("I failed!".into())
143        }
144
145        let mut world = World::default();
146        world.add_observer(system);
147        Schedule::default().run(&mut world);
148        world.trigger(TriggerEvent);
149    }
150
151    #[test]
152    fn test_fallible_observer_ignored_errors() {
153        #[derive(Resource, Default)]
154        struct Ran(bool);
155
156        fn system(_: On<TriggerEvent>, mut ran: ResMut<Ran>) -> Result {
157            ran.0 = true;
158            Err("I failed!".into())
159        }
160
161        // Using observer error handler
162        let mut world = World::default();
163        world.init_resource::<Ran>();
164        world.spawn(Observer::new(system).with_error_handler(ignore));
165        world.trigger(TriggerEvent);
166        assert!(world.resource::<Ran>().0);
167
168        // Using world error handler
169        let mut world = World::default();
170        world.init_resource::<Ran>();
171        world.spawn(Observer::new(system));
172        // Test that the correct handler is used when the observer was added
173        // before the fallback handler
174        world.insert_resource(FallbackErrorHandler(ignore));
175        world.trigger(TriggerEvent);
176        assert!(world.resource::<Ran>().0);
177    }
178
179    #[test]
180    #[should_panic]
181    fn exclusive_system_cannot_be_observer() {
182        fn system(_: On<TriggerEvent>, _world: &mut World) {}
183        let mut world = World::default();
184        world.add_observer(system);
185    }
186}