bevy_ecs/schedule/executor/
mod.rs1#[cfg(feature = "std")]
2mod multi_threaded;
3mod simple;
4mod single_threaded;
5
6use alloc::{vec, vec::Vec};
7use bevy_utils::prelude::DebugName;
8use core::any::TypeId;
9
10#[expect(deprecated, reason = "We still need to support this.")]
11pub use self::{simple::SimpleExecutor, single_threaded::SingleThreadedExecutor};
12
13#[cfg(feature = "std")]
14pub use self::multi_threaded::{MainThreadExecutor, MultiThreadedExecutor};
15
16use fixedbitset::FixedBitSet;
17
18use crate::{
19 component::{CheckChangeTicks, Tick},
20 error::{BevyError, ErrorContext, Result},
21 prelude::{IntoSystemSet, SystemSet},
22 query::FilteredAccessSet,
23 schedule::{
24 ConditionWithAccess, InternedSystemSet, SystemKey, SystemSetKey, SystemTypeSet,
25 SystemWithAccess,
26 },
27 system::{RunSystemError, System, SystemIn, SystemParamValidationError, SystemStateFlags},
28 world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
29};
30
31pub(super) trait SystemExecutor: Send + Sync {
33 fn kind(&self) -> ExecutorKind;
34 fn init(&mut self, schedule: &SystemSchedule);
35 fn run(
36 &mut self,
37 schedule: &mut SystemSchedule,
38 world: &mut World,
39 skip_systems: Option<&FixedBitSet>,
40 error_handler: fn(BevyError, ErrorContext),
41 );
42 fn set_apply_final_deferred(&mut self, value: bool);
43}
44
45#[derive(PartialEq, Eq, Default, Debug, Copy, Clone)]
51pub enum ExecutorKind {
52 #[cfg_attr(
57 any(
58 target_arch = "wasm32",
59 not(feature = "std"),
60 not(feature = "multi_threaded")
61 ),
62 default
63 )]
64 SingleThreaded,
65 #[deprecated(
68 since = "0.17.0",
69 note = "Use SingleThreaded instead. See https://github.com/bevyengine/bevy/issues/18453 for motivation."
70 )]
71 Simple,
72 #[cfg(feature = "std")]
74 #[cfg_attr(all(not(target_arch = "wasm32"), feature = "multi_threaded"), default)]
75 MultiThreaded,
76}
77
78#[derive(Default)]
84pub struct SystemSchedule {
85 pub(super) system_ids: Vec<SystemKey>,
87 pub(super) systems: Vec<SystemWithAccess>,
89 pub(super) system_conditions: Vec<Vec<ConditionWithAccess>>,
91 #[cfg_attr(
94 not(feature = "std"),
95 expect(dead_code, reason = "currently only used with the std feature")
96 )]
97 pub(super) system_dependencies: Vec<usize>,
98 #[cfg_attr(
101 not(feature = "std"),
102 expect(dead_code, reason = "currently only used with the std feature")
103 )]
104 pub(super) system_dependents: Vec<Vec<usize>>,
105 pub(super) sets_with_conditions_of_systems: Vec<FixedBitSet>,
108 pub(super) set_ids: Vec<SystemSetKey>,
110 pub(super) set_conditions: Vec<Vec<ConditionWithAccess>>,
112 pub(super) systems_in_sets_with_conditions: Vec<FixedBitSet>,
117}
118
119impl SystemSchedule {
120 pub const fn new() -> Self {
122 Self {
123 systems: Vec::new(),
124 system_conditions: Vec::new(),
125 set_conditions: Vec::new(),
126 system_ids: Vec::new(),
127 set_ids: Vec::new(),
128 system_dependencies: Vec::new(),
129 system_dependents: Vec::new(),
130 sets_with_conditions_of_systems: Vec::new(),
131 systems_in_sets_with_conditions: Vec::new(),
132 }
133 }
134}
135
136#[doc(alias = "apply_system_buffers")]
161pub struct ApplyDeferred;
162
163pub(super) fn is_apply_deferred(system: &dyn System<In = (), Out = ()>) -> bool {
165 system.type_id() == TypeId::of::<ApplyDeferred>()
166}
167
168impl System for ApplyDeferred {
169 type In = ();
170 type Out = ();
171
172 fn name(&self) -> DebugName {
173 DebugName::borrowed("bevy_ecs::apply_deferred")
174 }
175
176 fn flags(&self) -> SystemStateFlags {
177 SystemStateFlags::NON_SEND | SystemStateFlags::EXCLUSIVE
179 }
180
181 unsafe fn run_unsafe(
182 &mut self,
183 _input: SystemIn<'_, Self>,
184 _world: UnsafeWorldCell,
185 ) -> Result<Self::Out, RunSystemError> {
186 Ok(())
189 }
190
191 #[cfg(feature = "hotpatching")]
192 #[inline]
193 fn refresh_hotpatch(&mut self) {}
194
195 fn run(
196 &mut self,
197 _input: SystemIn<'_, Self>,
198 _world: &mut World,
199 ) -> Result<Self::Out, RunSystemError> {
200 Ok(())
203 }
204
205 fn apply_deferred(&mut self, _world: &mut World) {}
206
207 fn queue_deferred(&mut self, _world: DeferredWorld) {}
208
209 unsafe fn validate_param_unsafe(
210 &mut self,
211 _world: UnsafeWorldCell,
212 ) -> Result<(), SystemParamValidationError> {
213 Ok(())
216 }
217
218 fn initialize(&mut self, _world: &mut World) -> FilteredAccessSet {
219 FilteredAccessSet::new()
220 }
221
222 fn check_change_tick(&mut self, _check: CheckChangeTicks) {}
223
224 fn default_system_sets(&self) -> Vec<InternedSystemSet> {
225 vec![SystemTypeSet::<Self>::new().intern()]
226 }
227
228 fn get_last_run(&self) -> Tick {
229 Tick::MAX
231 }
232
233 fn set_last_run(&mut self, _last_run: Tick) {}
234}
235
236impl IntoSystemSet<()> for ApplyDeferred {
237 type Set = SystemTypeSet<Self>;
238
239 fn into_system_set(self) -> Self::Set {
240 SystemTypeSet::<Self>::new()
241 }
242}
243
244mod __rust_begin_short_backtrace {
253 use core::hint::black_box;
254
255 #[cfg(feature = "std")]
256 use crate::world::unsafe_world_cell::UnsafeWorldCell;
257 use crate::{
258 error::Result,
259 system::{ReadOnlySystem, RunSystemError, ScheduleSystem},
260 world::World,
261 };
262
263 #[cfg(feature = "std")]
267 #[inline(never)]
268 pub(super) unsafe fn run_unsafe(
269 system: &mut ScheduleSystem,
270 world: UnsafeWorldCell,
271 ) -> Result<(), RunSystemError> {
272 let result = system.run_unsafe((), world);
273 black_box(());
275 result
276 }
277
278 #[cfg(feature = "std")]
282 #[inline(never)]
283 pub(super) unsafe fn readonly_run_unsafe<O: 'static>(
284 system: &mut dyn ReadOnlySystem<In = (), Out = O>,
285 world: UnsafeWorldCell,
286 ) -> Result<O, RunSystemError> {
287 black_box(system.run_unsafe((), world))
289 }
290
291 #[inline(never)]
292 pub(super) fn run(
293 system: &mut ScheduleSystem,
294 world: &mut World,
295 ) -> Result<(), RunSystemError> {
296 let result = system.run((), world);
297 black_box(());
299 result
300 }
301
302 #[inline(never)]
303 pub(super) fn run_without_applying_deferred(
304 system: &mut ScheduleSystem,
305 world: &mut World,
306 ) -> Result<(), RunSystemError> {
307 let result = system.run_without_applying_deferred((), world);
308 black_box(());
310 result
311 }
312
313 #[inline(never)]
314 pub(super) fn readonly_run<O: 'static>(
315 system: &mut dyn ReadOnlySystem<In = (), Out = O>,
316 world: &mut World,
317 ) -> Result<O, RunSystemError> {
318 black_box(system.run((), world))
320 }
321}
322
323#[cfg(test)]
324mod tests {
325 use crate::{
326 prelude::{Component, In, IntoSystem, Resource, Schedule},
327 schedule::ExecutorKind,
328 system::{Populated, Res, ResMut, Single},
329 world::World,
330 };
331
332 #[derive(Component)]
333 struct TestComponent;
334
335 const EXECUTORS: [ExecutorKind; 3] = [
336 #[expect(deprecated, reason = "We still need to test this.")]
337 ExecutorKind::Simple,
338 ExecutorKind::SingleThreaded,
339 ExecutorKind::MultiThreaded,
340 ];
341
342 #[derive(Resource, Default)]
343 struct TestState {
344 populated_ran: bool,
345 single_ran: bool,
346 }
347
348 #[derive(Resource, Default)]
349 struct Counter(u8);
350
351 fn set_single_state(mut _single: Single<&TestComponent>, mut state: ResMut<TestState>) {
352 state.single_ran = true;
353 }
354
355 fn set_populated_state(
356 mut _populated: Populated<&TestComponent>,
357 mut state: ResMut<TestState>,
358 ) {
359 state.populated_ran = true;
360 }
361
362 #[test]
363 #[expect(clippy::print_stdout, reason = "std and println are allowed in tests")]
364 fn single_and_populated_skipped_and_run() {
365 for executor in EXECUTORS {
366 std::println!("Testing executor: {executor:?}");
367
368 let mut world = World::new();
369 world.init_resource::<TestState>();
370
371 let mut schedule = Schedule::default();
372 schedule.set_executor_kind(executor);
373 schedule.add_systems((set_single_state, set_populated_state));
374 schedule.run(&mut world);
375
376 let state = world.get_resource::<TestState>().unwrap();
377 assert!(!state.single_ran);
378 assert!(!state.populated_ran);
379
380 world.spawn(TestComponent);
381
382 schedule.run(&mut world);
383 let state = world.get_resource::<TestState>().unwrap();
384 assert!(state.single_ran);
385 assert!(state.populated_ran);
386 }
387 }
388
389 fn look_for_missing_resource(_res: Res<TestState>) {}
390
391 #[test]
392 #[should_panic]
393 fn missing_resource_panics_simple() {
394 let mut world = World::new();
395 let mut schedule = Schedule::default();
396
397 #[expect(deprecated, reason = "We still need to test this.")]
398 schedule.set_executor_kind(ExecutorKind::Simple);
399 schedule.add_systems(look_for_missing_resource);
400 schedule.run(&mut world);
401 }
402
403 #[test]
404 #[should_panic]
405 fn missing_resource_panics_single_threaded() {
406 let mut world = World::new();
407 let mut schedule = Schedule::default();
408
409 schedule.set_executor_kind(ExecutorKind::SingleThreaded);
410 schedule.add_systems(look_for_missing_resource);
411 schedule.run(&mut world);
412 }
413
414 #[test]
415 #[should_panic]
416 fn missing_resource_panics_multi_threaded() {
417 let mut world = World::new();
418 let mut schedule = Schedule::default();
419
420 schedule.set_executor_kind(ExecutorKind::MultiThreaded);
421 schedule.add_systems(look_for_missing_resource);
422 schedule.run(&mut world);
423 }
424
425 #[test]
426 fn piped_systems_first_system_skipped() {
427 fn pipe_out(_single: Single<&TestComponent>) -> u8 {
429 42
430 }
431
432 fn pipe_in(_input: In<u8>, mut counter: ResMut<Counter>) {
433 counter.0 += 1;
434 }
435
436 let mut world = World::new();
437 world.init_resource::<Counter>();
438 let mut schedule = Schedule::default();
439
440 schedule.add_systems(pipe_out.pipe(pipe_in));
441 schedule.run(&mut world);
442
443 let counter = world.resource::<Counter>();
444 assert_eq!(counter.0, 0);
445 }
446
447 #[test]
448 fn piped_system_second_system_skipped() {
449 fn pipe_out(mut counter: ResMut<Counter>) -> u8 {
451 counter.0 += 1;
452 42
453 }
454
455 fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>, mut counter: ResMut<Counter>) {
457 counter.0 += 1;
458 }
459
460 let mut world = World::new();
461 world.init_resource::<Counter>();
462 let mut schedule = Schedule::default();
463
464 schedule.add_systems(pipe_out.pipe(pipe_in));
465 schedule.run(&mut world);
466 let counter = world.resource::<Counter>();
467 assert_eq!(counter.0, 1);
468 }
469
470 #[test]
471 #[should_panic]
472 fn piped_system_first_system_panics() {
473 fn pipe_out(_res: Res<TestState>) -> u8 {
475 42
476 }
477
478 fn pipe_in(_input: In<u8>) {}
479
480 let mut world = World::new();
481 let mut schedule = Schedule::default();
482
483 schedule.add_systems(pipe_out.pipe(pipe_in));
484 schedule.run(&mut world);
485 }
486
487 #[test]
488 #[should_panic]
489 fn piped_system_second_system_panics() {
490 fn pipe_out() -> u8 {
491 42
492 }
493
494 fn pipe_in(_input: In<u8>, _res: Res<TestState>) {}
496
497 let mut world = World::new();
498 let mut schedule = Schedule::default();
499
500 schedule.add_systems(pipe_out.pipe(pipe_in));
501 schedule.run(&mut world);
502 }
503
504 #[test]
507 fn piped_system_skip_and_panic() {
508 fn pipe_out(_single: Single<&TestComponent>) -> u8 {
510 42
511 }
512
513 fn pipe_in(_input: In<u8>, _res: Res<TestState>) {}
515
516 let mut world = World::new();
517 let mut schedule = Schedule::default();
518
519 schedule.add_systems(pipe_out.pipe(pipe_in));
520 schedule.run(&mut world);
521 }
522
523 #[test]
524 #[should_panic]
525 fn piped_system_panic_and_skip() {
526 fn pipe_out(_res: Res<TestState>) -> u8 {
529 42
530 }
531
532 fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>) {}
534
535 let mut world = World::new();
536 let mut schedule = Schedule::default();
537
538 schedule.add_systems(pipe_out.pipe(pipe_in));
539 schedule.run(&mut world);
540 }
541
542 #[test]
543 #[should_panic]
544 fn piped_system_panic_and_panic() {
545 fn pipe_out(_res: Res<TestState>) -> u8 {
548 42
549 }
550
551 fn pipe_in(_input: In<u8>, _res: Res<TestState>) {}
553
554 let mut world = World::new();
555 let mut schedule = Schedule::default();
556
557 schedule.add_systems(pipe_out.pipe(pipe_in));
558 schedule.run(&mut world);
559 }
560
561 #[test]
562 fn piped_system_skip_and_skip() {
563 fn pipe_out(_single: Single<&TestComponent>, mut counter: ResMut<Counter>) -> u8 {
566 counter.0 += 1;
567 42
568 }
569
570 fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>, mut counter: ResMut<Counter>) {
572 counter.0 += 1;
573 }
574
575 let mut world = World::new();
576 world.init_resource::<Counter>();
577 let mut schedule = Schedule::default();
578
579 schedule.add_systems(pipe_out.pipe(pipe_in));
580 schedule.run(&mut world);
581
582 let counter = world.resource::<Counter>();
583 assert_eq!(counter.0, 0);
584 }
585}