bevy_ecs/schedule/executor/
mod.rs

1#[cfg(feature = "std")]
2mod multi_threaded;
3mod simple;
4mod single_threaded;
5
6use alloc::{borrow::Cow, vec, vec::Vec};
7use core::any::TypeId;
8
9pub use self::{simple::SimpleExecutor, single_threaded::SingleThreadedExecutor};
10
11#[cfg(feature = "std")]
12pub use self::multi_threaded::{MainThreadExecutor, MultiThreadedExecutor};
13
14use fixedbitset::FixedBitSet;
15
16use crate::{
17    archetype::ArchetypeComponentId,
18    component::{ComponentId, Tick},
19    error::{BevyError, ErrorContext, Result},
20    prelude::{IntoSystemSet, SystemSet},
21    query::Access,
22    schedule::{BoxedCondition, InternedSystemSet, NodeId, SystemTypeSet},
23    system::{ScheduleSystem, System, SystemIn, SystemParamValidationError},
24    world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
25};
26
27/// Types that can run a [`SystemSchedule`] on a [`World`].
28pub(super) trait SystemExecutor: Send + Sync {
29    fn kind(&self) -> ExecutorKind;
30    fn init(&mut self, schedule: &SystemSchedule);
31    fn run(
32        &mut self,
33        schedule: &mut SystemSchedule,
34        world: &mut World,
35        skip_systems: Option<&FixedBitSet>,
36        error_handler: fn(BevyError, ErrorContext),
37    );
38    fn set_apply_final_deferred(&mut self, value: bool);
39}
40
41/// Specifies how a [`Schedule`](super::Schedule) will be run.
42///
43/// The default depends on the target platform:
44///  - [`SingleThreaded`](ExecutorKind::SingleThreaded) on Wasm.
45///  - [`MultiThreaded`](ExecutorKind::MultiThreaded) everywhere else.
46#[derive(PartialEq, Eq, Default, Debug, Copy, Clone)]
47pub enum ExecutorKind {
48    /// Runs the schedule using a single thread.
49    ///
50    /// Useful if you're dealing with a single-threaded environment, saving your threads for
51    /// other things, or just trying minimize overhead.
52    #[cfg_attr(any(target_arch = "wasm32", not(feature = "multi_threaded")), default)]
53    SingleThreaded,
54    /// Like [`SingleThreaded`](ExecutorKind::SingleThreaded) but calls [`apply_deferred`](crate::system::System::apply_deferred)
55    /// immediately after running each system.
56    Simple,
57    /// Runs the schedule using a thread pool. Non-conflicting systems can run in parallel.
58    #[cfg(feature = "std")]
59    #[cfg_attr(all(not(target_arch = "wasm32"), feature = "multi_threaded"), default)]
60    MultiThreaded,
61}
62
63/// Holds systems and conditions of a [`Schedule`](super::Schedule) sorted in topological order
64/// (along with dependency information for `multi_threaded` execution).
65///
66/// Since the arrays are sorted in the same order, elements are referenced by their index.
67/// [`FixedBitSet`] is used as a smaller, more efficient substitute of `HashSet<usize>`.
68#[derive(Default)]
69pub struct SystemSchedule {
70    /// List of system node ids.
71    pub(super) system_ids: Vec<NodeId>,
72    /// Indexed by system node id.
73    pub(super) systems: Vec<ScheduleSystem>,
74    /// Indexed by system node id.
75    pub(super) system_conditions: Vec<Vec<BoxedCondition>>,
76    /// Indexed by system node id.
77    /// Number of systems that the system immediately depends on.
78    #[cfg_attr(
79        not(feature = "std"),
80        expect(dead_code, reason = "currently only used with the std feature")
81    )]
82    pub(super) system_dependencies: Vec<usize>,
83    /// Indexed by system node id.
84    /// List of systems that immediately depend on the system.
85    #[cfg_attr(
86        not(feature = "std"),
87        expect(dead_code, reason = "currently only used with the std feature")
88    )]
89    pub(super) system_dependents: Vec<Vec<usize>>,
90    /// Indexed by system node id.
91    /// List of sets containing the system that have conditions
92    pub(super) sets_with_conditions_of_systems: Vec<FixedBitSet>,
93    /// List of system set node ids.
94    pub(super) set_ids: Vec<NodeId>,
95    /// Indexed by system set node id.
96    pub(super) set_conditions: Vec<Vec<BoxedCondition>>,
97    /// Indexed by system set node id.
98    /// List of systems that are in sets that have conditions.
99    ///
100    /// If a set doesn't run because of its conditions, this is used to skip all systems in it.
101    pub(super) systems_in_sets_with_conditions: Vec<FixedBitSet>,
102}
103
104impl SystemSchedule {
105    /// Creates an empty [`SystemSchedule`].
106    pub const fn new() -> Self {
107        Self {
108            systems: Vec::new(),
109            system_conditions: Vec::new(),
110            set_conditions: Vec::new(),
111            system_ids: Vec::new(),
112            set_ids: Vec::new(),
113            system_dependencies: Vec::new(),
114            system_dependents: Vec::new(),
115            sets_with_conditions_of_systems: Vec::new(),
116            systems_in_sets_with_conditions: Vec::new(),
117        }
118    }
119}
120
121/// See [`ApplyDeferred`].
122#[deprecated(
123    since = "0.16.0",
124    note = "Use `ApplyDeferred` instead. This was previously a function but is now a marker struct System."
125)]
126#[expect(
127    non_upper_case_globals,
128    reason = "This item is deprecated; as such, its previous name needs to stay."
129)]
130pub const apply_deferred: ApplyDeferred = ApplyDeferred;
131
132/// A special [`System`] that instructs the executor to call
133/// [`System::apply_deferred`] on the systems that have run but not applied
134/// their [`Deferred`] system parameters (like [`Commands`]) or other system buffers.
135///
136/// ## Scheduling
137///
138/// `ApplyDeferred` systems are scheduled *by default*
139/// - later in the same schedule run (for example, if a system with `Commands` param
140///   is scheduled in `Update`, all the changes will be visible in `PostUpdate`)
141/// - between systems with dependencies if the dependency [has deferred buffers]
142///   (if system `bar` directly or indirectly depends on `foo`, and `foo` uses
143///   `Commands` param, changes to the world in `foo` will be visible in `bar`)
144///
145/// ## Notes
146/// - This system (currently) does nothing if it's called manually or wrapped
147///   inside a [`PipeSystem`].
148/// - Modifying a [`Schedule`] may change the order buffers are applied.
149///
150/// [`System::apply_deferred`]: crate::system::System::apply_deferred
151/// [`Deferred`]: crate::system::Deferred
152/// [`Commands`]: crate::prelude::Commands
153/// [has deferred buffers]: crate::system::System::has_deferred
154/// [`PipeSystem`]: crate::system::PipeSystem
155/// [`Schedule`]: super::Schedule
156#[doc(alias = "apply_system_buffers")]
157pub struct ApplyDeferred;
158
159/// Returns `true` if the [`System`] is an instance of [`ApplyDeferred`].
160pub(super) fn is_apply_deferred(system: &ScheduleSystem) -> bool {
161    system.type_id() == TypeId::of::<ApplyDeferred>()
162}
163
164impl System for ApplyDeferred {
165    type In = ();
166    type Out = Result<()>;
167
168    fn name(&self) -> Cow<'static, str> {
169        Cow::Borrowed("bevy_ecs::apply_deferred")
170    }
171
172    fn component_access(&self) -> &Access<ComponentId> {
173        // This system accesses no components.
174        const { &Access::new() }
175    }
176
177    fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
178        // This system accesses no archetype components.
179        const { &Access::new() }
180    }
181
182    fn is_send(&self) -> bool {
183        // Although this system itself does nothing on its own, the system
184        // executor uses it to apply deferred commands. Commands must be allowed
185        // to access non-send resources, so this system must be non-send for
186        // scheduling purposes.
187        false
188    }
189
190    fn is_exclusive(&self) -> bool {
191        // This system is labeled exclusive because it is used by the system
192        // executor to find places where deferred commands should be applied,
193        // and commands can only be applied with exclusive access to the world.
194        true
195    }
196
197    fn has_deferred(&self) -> bool {
198        // This system itself doesn't have any commands to apply, but when it
199        // is pulled from the schedule to be ran, the executor will apply
200        // deferred commands from other systems.
201        false
202    }
203
204    unsafe fn run_unsafe(
205        &mut self,
206        _input: SystemIn<'_, Self>,
207        _world: UnsafeWorldCell,
208    ) -> Self::Out {
209        // This system does nothing on its own. The executor will apply deferred
210        // commands from other systems instead of running this system.
211        Ok(())
212    }
213
214    fn run(&mut self, _input: SystemIn<'_, Self>, _world: &mut World) -> Self::Out {
215        // This system does nothing on its own. The executor will apply deferred
216        // commands from other systems instead of running this system.
217        Ok(())
218    }
219
220    fn apply_deferred(&mut self, _world: &mut World) {}
221
222    fn queue_deferred(&mut self, _world: DeferredWorld) {}
223
224    unsafe fn validate_param_unsafe(
225        &mut self,
226        _world: UnsafeWorldCell,
227    ) -> Result<(), SystemParamValidationError> {
228        // This system is always valid to run because it doesn't do anything,
229        // and only used as a marker for the executor.
230        Ok(())
231    }
232
233    fn initialize(&mut self, _world: &mut World) {}
234
235    fn update_archetype_component_access(&mut self, _world: UnsafeWorldCell) {}
236
237    fn check_change_tick(&mut self, _change_tick: Tick) {}
238
239    fn default_system_sets(&self) -> Vec<InternedSystemSet> {
240        vec![SystemTypeSet::<Self>::new().intern()]
241    }
242
243    fn get_last_run(&self) -> Tick {
244        // This system is never run, so it has no last run tick.
245        Tick::MAX
246    }
247
248    fn set_last_run(&mut self, _last_run: Tick) {}
249}
250
251impl IntoSystemSet<()> for ApplyDeferred {
252    type Set = SystemTypeSet<Self>;
253
254    fn into_system_set(self) -> Self::Set {
255        SystemTypeSet::<Self>::new()
256    }
257}
258
259/// These functions hide the bottom of the callstack from `RUST_BACKTRACE=1` (assuming the default panic handler is used).
260///
261/// The full callstack will still be visible with `RUST_BACKTRACE=full`.
262/// They are specialized for `System::run` & co instead of being generic over closures because this avoids an
263/// extra frame in the backtrace.
264///
265/// This is reliant on undocumented behavior in Rust's default panic handler, which checks the call stack for symbols
266/// containing the string `__rust_begin_short_backtrace` in their mangled name.
267mod __rust_begin_short_backtrace {
268    use core::hint::black_box;
269
270    use crate::{
271        error::Result,
272        system::{ReadOnlySystem, ScheduleSystem},
273        world::{unsafe_world_cell::UnsafeWorldCell, World},
274    };
275
276    /// # Safety
277    /// See `System::run_unsafe`.
278    #[inline(never)]
279    pub(super) unsafe fn run_unsafe(system: &mut ScheduleSystem, world: UnsafeWorldCell) -> Result {
280        let result = system.run_unsafe((), world);
281        black_box(());
282        result
283    }
284
285    /// # Safety
286    /// See `ReadOnlySystem::run_unsafe`.
287    #[cfg_attr(
288        not(feature = "std"),
289        expect(dead_code, reason = "currently only used with the std feature")
290    )]
291    #[inline(never)]
292    pub(super) unsafe fn readonly_run_unsafe<O: 'static>(
293        system: &mut dyn ReadOnlySystem<In = (), Out = O>,
294        world: UnsafeWorldCell,
295    ) -> O {
296        black_box(system.run_unsafe((), world))
297    }
298
299    #[inline(never)]
300    pub(super) fn run(system: &mut ScheduleSystem, world: &mut World) -> Result {
301        let result = system.run((), world);
302        black_box(());
303        result
304    }
305
306    #[inline(never)]
307    pub(super) fn readonly_run<O: 'static>(
308        system: &mut dyn ReadOnlySystem<In = (), Out = O>,
309        world: &mut World,
310    ) -> O {
311        black_box(system.run((), world))
312    }
313}
314
315#[cfg(test)]
316mod tests {
317    use crate::{
318        prelude::{Component, In, IntoSystem, Resource, Schedule},
319        schedule::ExecutorKind,
320        system::{Populated, Res, ResMut, Single},
321        world::World,
322    };
323
324    #[derive(Component)]
325    struct TestComponent;
326
327    const EXECUTORS: [ExecutorKind; 3] = [
328        ExecutorKind::Simple,
329        ExecutorKind::SingleThreaded,
330        ExecutorKind::MultiThreaded,
331    ];
332
333    #[derive(Resource, Default)]
334    struct TestState {
335        populated_ran: bool,
336        single_ran: bool,
337    }
338
339    #[derive(Resource, Default)]
340    struct Counter(u8);
341
342    fn set_single_state(mut _single: Single<&TestComponent>, mut state: ResMut<TestState>) {
343        state.single_ran = true;
344    }
345
346    fn set_populated_state(
347        mut _populated: Populated<&TestComponent>,
348        mut state: ResMut<TestState>,
349    ) {
350        state.populated_ran = true;
351    }
352
353    #[test]
354    #[expect(clippy::print_stdout, reason = "std and println are allowed in tests")]
355    fn single_and_populated_skipped_and_run() {
356        for executor in EXECUTORS {
357            std::println!("Testing executor: {:?}", executor);
358
359            let mut world = World::new();
360            world.init_resource::<TestState>();
361
362            let mut schedule = Schedule::default();
363            schedule.set_executor_kind(executor);
364            schedule.add_systems((set_single_state, set_populated_state));
365            schedule.run(&mut world);
366
367            let state = world.get_resource::<TestState>().unwrap();
368            assert!(!state.single_ran);
369            assert!(!state.populated_ran);
370
371            world.spawn(TestComponent);
372
373            schedule.run(&mut world);
374            let state = world.get_resource::<TestState>().unwrap();
375            assert!(state.single_ran);
376            assert!(state.populated_ran);
377        }
378    }
379
380    fn look_for_missing_resource(_res: Res<TestState>) {}
381
382    #[test]
383    #[should_panic]
384    fn missing_resource_panics_simple() {
385        let mut world = World::new();
386        let mut schedule = Schedule::default();
387
388        schedule.set_executor_kind(ExecutorKind::Simple);
389        schedule.add_systems(look_for_missing_resource);
390        schedule.run(&mut world);
391    }
392
393    #[test]
394    #[should_panic]
395    fn missing_resource_panics_single_threaded() {
396        let mut world = World::new();
397        let mut schedule = Schedule::default();
398
399        schedule.set_executor_kind(ExecutorKind::SingleThreaded);
400        schedule.add_systems(look_for_missing_resource);
401        schedule.run(&mut world);
402    }
403
404    #[test]
405    #[should_panic]
406    fn missing_resource_panics_multi_threaded() {
407        let mut world = World::new();
408        let mut schedule = Schedule::default();
409
410        schedule.set_executor_kind(ExecutorKind::MultiThreaded);
411        schedule.add_systems(look_for_missing_resource);
412        schedule.run(&mut world);
413    }
414
415    #[test]
416    fn piped_systems_first_system_skipped() {
417        // This system should be skipped when run due to no matching entity
418        fn pipe_out(_single: Single<&TestComponent>) -> u8 {
419            42
420        }
421
422        fn pipe_in(_input: In<u8>, mut counter: ResMut<Counter>) {
423            counter.0 += 1;
424        }
425
426        let mut world = World::new();
427        world.init_resource::<Counter>();
428        let mut schedule = Schedule::default();
429
430        schedule.add_systems(pipe_out.pipe(pipe_in));
431        schedule.run(&mut world);
432
433        let counter = world.resource::<Counter>();
434        assert_eq!(counter.0, 0);
435    }
436
437    #[test]
438    fn piped_system_second_system_skipped() {
439        fn pipe_out(mut counter: ResMut<Counter>) -> u8 {
440            counter.0 += 1;
441            42
442        }
443
444        // This system should be skipped when run due to no matching entity
445        fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>) {}
446
447        let mut world = World::new();
448        world.init_resource::<Counter>();
449        let mut schedule = Schedule::default();
450
451        schedule.add_systems(pipe_out.pipe(pipe_in));
452        schedule.run(&mut world);
453        let counter = world.resource::<Counter>();
454        assert_eq!(counter.0, 0);
455    }
456
457    #[test]
458    #[should_panic]
459    fn piped_system_first_system_panics() {
460        // This system should panic when run because the resource is missing
461        fn pipe_out(_res: Res<TestState>) -> u8 {
462            42
463        }
464
465        fn pipe_in(_input: In<u8>) {}
466
467        let mut world = World::new();
468        let mut schedule = Schedule::default();
469
470        schedule.add_systems(pipe_out.pipe(pipe_in));
471        schedule.run(&mut world);
472    }
473
474    #[test]
475    #[should_panic]
476    fn piped_system_second_system_panics() {
477        fn pipe_out() -> u8 {
478            42
479        }
480
481        // This system should panic when run because the resource is missing
482        fn pipe_in(_input: In<u8>, _res: Res<TestState>) {}
483
484        let mut world = World::new();
485        let mut schedule = Schedule::default();
486
487        schedule.add_systems(pipe_out.pipe(pipe_in));
488        schedule.run(&mut world);
489    }
490
491    // This test runs without panicking because we've
492    // decided to use early-out behavior for piped systems
493    #[test]
494    fn piped_system_skip_and_panic() {
495        // This system should be skipped when run due to no matching entity
496        fn pipe_out(_single: Single<&TestComponent>) -> u8 {
497            42
498        }
499
500        // This system should panic when run because the resource is missing
501        fn pipe_in(_input: In<u8>, _res: Res<TestState>) {}
502
503        let mut world = World::new();
504        let mut schedule = Schedule::default();
505
506        schedule.add_systems(pipe_out.pipe(pipe_in));
507        schedule.run(&mut world);
508    }
509
510    #[test]
511    #[should_panic]
512    fn piped_system_panic_and_skip() {
513        // This system should panic when run because the resource is missing
514
515        fn pipe_out(_res: Res<TestState>) -> u8 {
516            42
517        }
518
519        // This system should be skipped when run due to no matching entity
520        fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>) {}
521
522        let mut world = World::new();
523        let mut schedule = Schedule::default();
524
525        schedule.add_systems(pipe_out.pipe(pipe_in));
526        schedule.run(&mut world);
527    }
528
529    #[test]
530    #[should_panic]
531    fn piped_system_panic_and_panic() {
532        // This system should panic when run because the resource is missing
533
534        fn pipe_out(_res: Res<TestState>) -> u8 {
535            42
536        }
537
538        // This system should panic when run because the resource is missing
539        fn pipe_in(_input: In<u8>, _res: Res<TestState>) {}
540
541        let mut world = World::new();
542        let mut schedule = Schedule::default();
543
544        schedule.add_systems(pipe_out.pipe(pipe_in));
545        schedule.run(&mut world);
546    }
547
548    #[test]
549    fn piped_system_skip_and_skip() {
550        // This system should be skipped when run due to no matching entity
551
552        fn pipe_out(_single: Single<&TestComponent>, mut counter: ResMut<Counter>) -> u8 {
553            counter.0 += 1;
554            42
555        }
556
557        // This system should be skipped when run due to no matching entity
558        fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>, mut counter: ResMut<Counter>) {
559            counter.0 += 1;
560        }
561
562        let mut world = World::new();
563        world.init_resource::<Counter>();
564        let mut schedule = Schedule::default();
565
566        schedule.add_systems(pipe_out.pipe(pipe_in));
567        schedule.run(&mut world);
568
569        let counter = world.resource::<Counter>();
570        assert_eq!(counter.0, 0);
571    }
572}