bevy_ecs/schedule/executor/
mod.rs1#[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
27pub(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#[derive(PartialEq, Eq, Default, Debug, Copy, Clone)]
47pub enum ExecutorKind {
48 #[cfg_attr(any(target_arch = "wasm32", not(feature = "multi_threaded")), default)]
53 SingleThreaded,
54 Simple,
57 #[cfg(feature = "std")]
59 #[cfg_attr(all(not(target_arch = "wasm32"), feature = "multi_threaded"), default)]
60 MultiThreaded,
61}
62
63#[derive(Default)]
69pub struct SystemSchedule {
70 pub(super) system_ids: Vec<NodeId>,
72 pub(super) systems: Vec<ScheduleSystem>,
74 pub(super) system_conditions: Vec<Vec<BoxedCondition>>,
76 #[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 #[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 pub(super) sets_with_conditions_of_systems: Vec<FixedBitSet>,
93 pub(super) set_ids: Vec<NodeId>,
95 pub(super) set_conditions: Vec<Vec<BoxedCondition>>,
97 pub(super) systems_in_sets_with_conditions: Vec<FixedBitSet>,
102}
103
104impl SystemSchedule {
105 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#[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#[doc(alias = "apply_system_buffers")]
157pub struct ApplyDeferred;
158
159pub(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 const { &Access::new() }
175 }
176
177 fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
178 const { &Access::new() }
180 }
181
182 fn is_send(&self) -> bool {
183 false
188 }
189
190 fn is_exclusive(&self) -> bool {
191 true
195 }
196
197 fn has_deferred(&self) -> bool {
198 false
202 }
203
204 unsafe fn run_unsafe(
205 &mut self,
206 _input: SystemIn<'_, Self>,
207 _world: UnsafeWorldCell,
208 ) -> Self::Out {
209 Ok(())
212 }
213
214 fn run(&mut self, _input: SystemIn<'_, Self>, _world: &mut World) -> Self::Out {
215 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 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 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
259mod __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 #[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 #[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 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 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 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 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 #[test]
494 fn piped_system_skip_and_panic() {
495 fn pipe_out(_single: Single<&TestComponent>) -> u8 {
497 42
498 }
499
500 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 fn pipe_out(_res: Res<TestState>) -> u8 {
516 42
517 }
518
519 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 fn pipe_out(_res: Res<TestState>) -> u8 {
535 42
536 }
537
538 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 fn pipe_out(_single: Single<&TestComponent>, mut counter: ResMut<Counter>) -> u8 {
553 counter.0 += 1;
554 42
555 }
556
557 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}