#[cfg(feature = "std")]
mod multi_threaded;
mod single_threaded;
use alloc::{boxed::Box, vec, vec::Vec};
use bevy_utils::prelude::DebugName;
use core::any::TypeId;
pub use self::single_threaded::SingleThreadedExecutor;
#[cfg(feature = "std")]
pub use self::multi_threaded::{MainThreadExecutor, MultiThreadedExecutor};
pub use fixedbitset::FixedBitSet;
use crate::{
change_detection::{CheckChangeTicks, Tick},
error::{BevyError, ErrorContext, Result},
prelude::{IntoSystemSet, SystemSet},
query::FilteredAccessSet,
schedule::{
ConditionWithAccess, InternedSystemSet, SystemKey, SystemSetKey, SystemTypeSet,
SystemWithAccess,
},
system::{RunSystemError, System, SystemIn, SystemStateFlags},
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
};
pub trait SystemExecutor: Send + Sync {
fn init(&mut self, schedule: &SystemSchedule);
fn run(
&mut self,
schedule: &mut SystemSchedule,
world: &mut World,
skip_systems: Option<&FixedBitSet>,
error_handler: fn(BevyError, ErrorContext),
);
fn set_apply_final_deferred(&mut self, value: bool);
}
pub fn default_executor() -> Box<dyn SystemExecutor> {
#[cfg(all(
not(target_arch = "wasm32"),
feature = "std",
feature = "multi_threaded"
))]
{
Box::new(MultiThreadedExecutor::new())
}
#[cfg(any(
target_arch = "wasm32",
not(feature = "std"),
not(feature = "multi_threaded")
))]
{
Box::new(SingleThreadedExecutor::new())
}
}
#[derive(Default)]
pub struct SystemSchedule {
pub(super) system_ids: Vec<SystemKey>,
pub(super) systems: Vec<SystemWithAccess>,
pub(super) system_conditions: Vec<Vec<ConditionWithAccess>>,
#[cfg_attr(
not(feature = "std"),
expect(dead_code, reason = "currently only used with the std feature")
)]
pub(super) system_dependencies: Vec<usize>,
#[cfg_attr(
not(feature = "std"),
expect(dead_code, reason = "currently only used with the std feature")
)]
pub(super) system_dependents: Vec<Vec<usize>>,
pub(super) sets_with_conditions_of_systems: Vec<FixedBitSet>,
pub(super) set_ids: Vec<SystemSetKey>,
pub(super) set_conditions: Vec<Vec<ConditionWithAccess>>,
pub(super) systems_in_sets_with_conditions: Vec<FixedBitSet>,
}
impl SystemSchedule {
pub const fn new() -> Self {
Self {
systems: Vec::new(),
system_conditions: Vec::new(),
set_conditions: Vec::new(),
system_ids: Vec::new(),
set_ids: Vec::new(),
system_dependencies: Vec::new(),
system_dependents: Vec::new(),
sets_with_conditions_of_systems: Vec::new(),
systems_in_sets_with_conditions: Vec::new(),
}
}
pub unsafe fn systems_mut(&mut self) -> &mut Vec<SystemWithAccess> {
&mut self.systems
}
}
#[doc(alias = "apply_system_buffers")]
pub struct ApplyDeferred;
pub(super) fn is_apply_deferred(system: &dyn System<In = (), Out = ()>) -> bool {
system.system_type() == TypeId::of::<ApplyDeferred>()
}
impl System for ApplyDeferred {
type In = ();
type Out = ();
fn name(&self) -> DebugName {
DebugName::borrowed("bevy_ecs::apply_deferred")
}
fn flags(&self) -> SystemStateFlags {
SystemStateFlags::NON_SEND | SystemStateFlags::EXCLUSIVE
}
unsafe fn run_unsafe(
&mut self,
_input: SystemIn<'_, Self>,
_world: UnsafeWorldCell,
) -> Result<Self::Out, RunSystemError> {
Ok(())
}
#[cfg(feature = "hotpatching")]
#[inline]
fn refresh_hotpatch(&mut self) {}
fn run(
&mut self,
_input: SystemIn<'_, Self>,
_world: &mut World,
) -> Result<Self::Out, RunSystemError> {
Ok(())
}
fn apply_deferred(&mut self, _world: &mut World) {}
fn queue_deferred(&mut self, _world: DeferredWorld) {}
fn initialize(&mut self, _world: &mut World) -> FilteredAccessSet {
FilteredAccessSet::new()
}
fn check_change_tick(&mut self, _check: CheckChangeTicks) {}
fn default_system_sets(&self) -> Vec<InternedSystemSet> {
vec![SystemTypeSet::<Self>::new().intern()]
}
fn get_last_run(&self) -> Tick {
Tick::MAX
}
fn set_last_run(&mut self, _last_run: Tick) {}
}
impl IntoSystemSet<()> for ApplyDeferred {
type Set = SystemTypeSet<Self>;
fn into_system_set(self) -> Self::Set {
SystemTypeSet::<Self>::new()
}
}
mod __rust_begin_short_backtrace {
use core::hint::black_box;
#[cfg(feature = "std")]
use crate::world::unsafe_world_cell::UnsafeWorldCell;
use crate::{
error::Result,
system::{ReadOnlySystem, RunSystemError, ScheduleSystem},
world::World,
};
#[cfg(feature = "std")]
#[inline(never)]
pub(super) unsafe fn run_unsafe(
system: &mut ScheduleSystem,
world: UnsafeWorldCell,
) -> Result<(), RunSystemError> {
let result = unsafe { system.run_unsafe((), world) };
black_box(());
result
}
#[cfg(feature = "std")]
#[inline(never)]
pub(super) unsafe fn readonly_run_unsafe<O: 'static>(
system: &mut dyn ReadOnlySystem<In = (), Out = O>,
world: UnsafeWorldCell,
) -> Result<O, RunSystemError> {
black_box(unsafe { system.run_unsafe((), world) })
}
#[cfg(feature = "std")]
#[inline(never)]
pub(super) fn run(
system: &mut ScheduleSystem,
world: &mut World,
) -> Result<(), RunSystemError> {
let result = system.run((), world);
black_box(());
result
}
#[inline(never)]
pub(super) fn run_without_applying_deferred(
system: &mut ScheduleSystem,
world: &mut World,
) -> Result<(), RunSystemError> {
let result = system.run_without_applying_deferred((), world);
black_box(());
result
}
#[inline(never)]
pub(super) fn readonly_run<O: 'static>(
system: &mut dyn ReadOnlySystem<In = (), Out = O>,
world: &mut World,
) -> Result<O, RunSystemError> {
black_box(system.run((), world))
}
}
#[cfg(test)]
mod tests {
use crate::{
prelude::{Component, In, IntoSystem, Resource, Schedule},
schedule::{MultiThreadedExecutor, SingleThreadedExecutor},
system::{Populated, Res, ResMut, Single},
world::World,
};
#[derive(Component)]
struct TestComponent;
#[derive(Resource, Default)]
struct TestState {
populated_ran: bool,
single_ran: bool,
}
#[derive(Resource, Default)]
struct Counter(u8);
fn set_single_state(mut _single: Single<&TestComponent>, mut state: ResMut<TestState>) {
state.single_ran = true;
}
fn set_populated_state(
mut _populated: Populated<&TestComponent>,
mut state: ResMut<TestState>,
) {
state.populated_ran = true;
}
#[test]
fn single_and_populated_skipped_and_run_singlethreaded() {
let mut schedule = Schedule::default();
schedule.set_executor(SingleThreadedExecutor::new());
single_and_populated_skipped_and_run("SingleThreaded", schedule);
}
#[test]
fn single_and_populated_skipped_and_run_multithreaded() {
let mut schedule = Schedule::default();
schedule.set_executor(MultiThreadedExecutor::new());
single_and_populated_skipped_and_run("MultiThreaded", schedule);
}
#[expect(clippy::print_stdout, reason = "std and println are allowed in tests")]
fn single_and_populated_skipped_and_run(name: &str, mut schedule: Schedule) {
std::println!("Testing executor: {name}");
let mut world = World::new();
world.init_resource::<TestState>();
schedule.add_systems((set_single_state, set_populated_state));
schedule.run(&mut world);
let state = world.get_resource::<TestState>().unwrap();
assert!(!state.single_ran);
assert!(!state.populated_ran);
world.spawn(TestComponent);
schedule.run(&mut world);
let state = world.get_resource::<TestState>().unwrap();
assert!(state.single_ran);
assert!(state.populated_ran);
}
fn look_for_missing_resource(_res: Res<TestState>) {}
#[test]
#[should_panic]
fn missing_resource_panics_single_threaded() {
let mut world = World::new();
let mut schedule = Schedule::default();
schedule.set_executor(SingleThreadedExecutor::new());
schedule.add_systems(look_for_missing_resource);
schedule.run(&mut world);
}
#[test]
#[should_panic]
fn missing_resource_panics_multi_threaded() {
let mut world = World::new();
let mut schedule = Schedule::default();
schedule.set_executor(MultiThreadedExecutor::new());
schedule.add_systems(look_for_missing_resource);
schedule.run(&mut world);
}
#[test]
fn piped_systems_first_system_skipped() {
fn pipe_out(_single: Single<&TestComponent>) -> u8 {
42
}
fn pipe_in(_input: In<u8>, mut counter: ResMut<Counter>) {
counter.0 += 1;
}
let mut world = World::new();
world.init_resource::<Counter>();
let mut schedule = Schedule::default();
schedule.add_systems(pipe_out.pipe(pipe_in));
schedule.run(&mut world);
let counter = world.resource::<Counter>();
assert_eq!(counter.0, 0);
}
#[test]
fn piped_system_second_system_skipped() {
fn pipe_out(mut counter: ResMut<Counter>) -> u8 {
counter.0 += 1;
42
}
fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>, mut counter: ResMut<Counter>) {
counter.0 += 1;
}
let mut world = World::new();
world.init_resource::<Counter>();
let mut schedule = Schedule::default();
schedule.add_systems(pipe_out.pipe(pipe_in));
schedule.run(&mut world);
let counter = world.resource::<Counter>();
assert_eq!(counter.0, 1);
}
#[test]
#[should_panic]
fn piped_system_first_system_panics() {
fn pipe_out(_res: Res<TestState>) -> u8 {
42
}
fn pipe_in(_input: In<u8>) {}
let mut world = World::new();
let mut schedule = Schedule::default();
schedule.add_systems(pipe_out.pipe(pipe_in));
schedule.run(&mut world);
}
#[test]
#[should_panic]
fn piped_system_second_system_panics() {
fn pipe_out() -> u8 {
42
}
fn pipe_in(_input: In<u8>, _res: Res<TestState>) {}
let mut world = World::new();
let mut schedule = Schedule::default();
schedule.add_systems(pipe_out.pipe(pipe_in));
schedule.run(&mut world);
}
#[test]
fn piped_system_skip_and_panic() {
fn pipe_out(_single: Single<&TestComponent>) -> u8 {
42
}
fn pipe_in(_input: In<u8>, _res: Res<TestState>) {}
let mut world = World::new();
let mut schedule = Schedule::default();
schedule.add_systems(pipe_out.pipe(pipe_in));
schedule.run(&mut world);
}
#[test]
#[should_panic]
fn piped_system_panic_and_skip() {
fn pipe_out(_res: Res<TestState>) -> u8 {
42
}
fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>) {}
let mut world = World::new();
let mut schedule = Schedule::default();
schedule.add_systems(pipe_out.pipe(pipe_in));
schedule.run(&mut world);
}
#[test]
#[should_panic]
fn piped_system_panic_and_panic() {
fn pipe_out(_res: Res<TestState>) -> u8 {
42
}
fn pipe_in(_input: In<u8>, _res: Res<TestState>) {}
let mut world = World::new();
let mut schedule = Schedule::default();
schedule.add_systems(pipe_out.pipe(pipe_in));
schedule.run(&mut world);
}
#[test]
fn piped_system_skip_and_skip() {
fn pipe_out(_single: Single<&TestComponent>, mut counter: ResMut<Counter>) -> u8 {
counter.0 += 1;
42
}
fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>, mut counter: ResMut<Counter>) {
counter.0 += 1;
}
let mut world = World::new();
world.init_resource::<Counter>();
let mut schedule = Schedule::default();
schedule.add_systems(pipe_out.pipe(pipe_in));
schedule.run(&mut world);
let counter = world.resource::<Counter>();
assert_eq!(counter.0, 0);
}
}
#[cfg(test)]
mod validation_tests {
use crate::{
prelude::{Component, In, IntoSystem, Resource, Schedule},
schedule::{MultiThreadedExecutor, SingleThreadedExecutor},
system::{
DynParamBuilder, DynSystemParam, ExclusiveSystemParam, Local, ParamBuilder, ParamSet,
Query, Res, ResMut, RunSystemError, RunSystemOnce, Single, SystemMeta,
SystemParamBuilder, SystemParamValidationError,
},
world::World,
};
#[derive(Component)]
struct TestComponent;
#[derive(Resource, Default)]
struct Counter(u8);
#[derive(Resource)]
struct MissingResource;
struct AlwaysInvalid;
impl ExclusiveSystemParam for AlwaysInvalid {
type State = ();
type Item<'s> = AlwaysInvalid;
fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {}
fn get_param<'s>(
_state: &'s mut Self::State,
_system_meta: &SystemMeta,
) -> Result<Self::Item<'s>, SystemParamValidationError> {
Err(SystemParamValidationError::invalid::<Self>(
"always invalid",
))
}
}
#[test]
fn function_system_validation_failure_is_error() {
fn system(_res: Res<MissingResource>) {}
let mut world = World::new();
let result = world.run_system_once(system);
assert!(
matches!(result, Err(RunSystemError::Failed(_))),
"Expected Failed, got {result:?}"
);
}
#[test]
fn function_system_validation_skip() {
fn system(_single: Single<&TestComponent>) {}
let mut world = World::new();
let result = world.run_system_once(system);
assert!(
matches!(result, Err(RunSystemError::Skipped(_))),
"Expected Skipped, got {result:?}"
);
}
#[test]
fn function_system_validation_success() {
fn system(_res: Res<Counter>) {}
let mut world = World::new();
world.init_resource::<Counter>();
let result = world.run_system_once(system);
assert!(result.is_ok(), "Expected Ok, got {result:?}");
}
#[test]
fn adapter_system_validation_failure() {
fn system(_res: Res<MissingResource>) -> u32 {
42
}
let mut world = World::new();
let result = world.run_system_once(system.map(|_x| {}));
assert!(
matches!(result, Err(RunSystemError::Failed(_))),
"Expected Failed from adapter system, got {result:?}"
);
}
#[test]
fn adapter_system_validation_skip() {
fn system(_single: Single<&TestComponent>) -> u32 {
42
}
let mut world = World::new();
let result = world.run_system_once(system.map(|_x| {}));
assert!(
matches!(result, Err(RunSystemError::Skipped(_))),
"Expected Skipped from adapter system, got {result:?}"
);
}
#[test]
fn pipe_system_validation_failure_in_first() {
fn first(_res: Res<MissingResource>) -> u32 {
42
}
fn second(_input: In<u32>) {}
let mut world = World::new();
let result = world.run_system_once(first.pipe(second));
assert!(
matches!(result, Err(RunSystemError::Failed(_))),
"Expected Failed from pipe first system, got {result:?}"
);
}
#[test]
fn pipe_system_validation_failure_in_second() {
fn first() -> u32 {
42
}
fn second(_input: In<u32>, _res: Res<MissingResource>) {}
let mut world = World::new();
let result = world.run_system_once(first.pipe(second));
assert!(
matches!(result, Err(RunSystemError::Failed(_))),
"Expected Failed from pipe second system, got {result:?}"
);
}
#[test]
fn pipe_system_validation_skip_in_first() {
fn first(_single: Single<&TestComponent>) -> u32 {
42
}
fn second(_input: In<u32>) {}
let mut world = World::new();
let result = world.run_system_once(first.pipe(second));
assert!(
matches!(result, Err(RunSystemError::Skipped(_))),
"Expected Skipped from pipe first system, got {result:?}"
);
}
#[test]
fn pipe_system_validation_skip_in_second() {
fn first() -> u32 {
42
}
fn second(_input: In<u32>, _single: Single<&TestComponent>) {}
let mut world = World::new();
let result = world.run_system_once(first.pipe(second));
assert!(
matches!(result, Err(RunSystemError::Skipped(_))),
"Expected Skipped from pipe second system, got {result:?}"
);
}
#[test]
fn builder_system_validation_failure() {
fn system(_res: Res<MissingResource>) {}
let mut world = World::new();
let result = world.run_system_once(ParamBuilder.build_system(system));
assert!(
matches!(result, Err(RunSystemError::Failed(_))),
"Expected Failed from builder system, got {result:?}"
);
}
#[test]
fn builder_system_validation_skip() {
fn system(_single: Single<&TestComponent>) {}
let mut world = World::new();
let result = world.run_system_once(ParamBuilder.build_system(system));
assert!(
matches!(result, Err(RunSystemError::Skipped(_))),
"Expected Skipped from builder system, got {result:?}"
);
}
#[test]
fn dyn_system_param_validation_failure() {
let mut world = World::new();
let system = (DynParamBuilder::new::<Res<MissingResource>>(ParamBuilder),)
.build_state(&mut world)
.build_system(|_param: DynSystemParam| {});
let result = world.run_system_once(system);
assert!(
matches!(result, Err(RunSystemError::Failed(_))),
"Expected Failed from DynSystemParam system, got {result:?}"
);
}
#[test]
fn dyn_system_param_validation_skip() {
let mut world = World::new();
let system = (DynParamBuilder::new::<Single<&TestComponent>>(ParamBuilder),)
.build_state(&mut world)
.build_system(|_param: DynSystemParam| {});
let result = world.run_system_once(system);
assert!(
matches!(result, Err(RunSystemError::Skipped(_))),
"Expected Skipped from DynSystemParam system, got {result:?}"
);
}
#[test]
fn dyn_system_param_validation_success() {
let mut world = World::new();
world.init_resource::<Counter>();
let system = (DynParamBuilder::new::<Res<Counter>>(ParamBuilder),)
.build_state(&mut world)
.build_system(|_param: DynSystemParam| {});
let result = world.run_system_once(system);
assert!(
result.is_ok(),
"Expected Ok from DynSystemParam system, got {result:?}"
);
}
#[test]
fn exclusive_system_validation_failure() {
fn system(_world: &mut World, _param: AlwaysInvalid) {}
let mut world = World::new();
let result = world.run_system_once(system);
assert!(
matches!(result, Err(RunSystemError::Failed(_))),
"Expected Failed from exclusive system, got {result:?}"
);
}
#[test]
fn exclusive_system_validation_success() {
fn system(_world: &mut World, mut _local: Local<u32>) {}
let mut world = World::new();
let result = world.run_system_once(system);
assert!(
result.is_ok(),
"Expected Ok from exclusive system, got {result:?}"
);
}
#[test]
fn validation_skips_system_in_schedule_singlethreaded() {
let mut schedule = Schedule::default();
schedule.set_executor(SingleThreadedExecutor::new());
validation_skips_system_in_schedule("SingleThreaded", schedule);
}
#[test]
fn validation_skips_system_in_schedule_multithreaded() {
let mut schedule = Schedule::default();
schedule.set_executor(MultiThreadedExecutor::new());
validation_skips_system_in_schedule("MultiThreaded", schedule);
}
fn validation_skips_system_in_schedule(name: &str, mut schedule: Schedule) {
fn skippable_system(_single: Single<&TestComponent>, mut counter: ResMut<Counter>) {
counter.0 += 1;
}
let mut world = World::new();
world.init_resource::<Counter>();
schedule.add_systems(skippable_system);
schedule.run(&mut world);
assert_eq!(
world.resource::<Counter>().0,
0,
"System should have been skipped with {name}"
);
}
#[test]
fn param_set_validation_skip() {
fn system(mut _set: ParamSet<(Single<&TestComponent>,)>) {}
let mut world = World::new();
let result = world.run_system_once(system);
assert!(
matches!(result, Err(RunSystemError::Skipped(_))),
"Expected Skipped from ParamSet with invalid Single, got {result:?}"
);
}
#[test]
fn param_set_validation_failure() {
fn system(mut _set: ParamSet<(Query<&TestComponent>, Res<MissingResource>)>) {}
let mut world = World::new();
let result = world.run_system_once(system);
assert!(
matches!(result, Err(RunSystemError::Failed(_))),
"Expected Failed from ParamSet with missing resource, got {result:?}"
);
}
#[test]
fn param_set_validation_success() {
fn system(mut set: ParamSet<(Query<&TestComponent>, Res<Counter>)>) {
let _q = set.p0();
}
let mut world = World::new();
world.init_resource::<Counter>();
let result = world.run_system_once(system);
assert!(
result.is_ok(),
"Expected Ok from ParamSet with valid params, got {result:?}"
);
}
}