Path: blob/main/crates/bevy_ecs/src/schedule/executor/single_threaded.rs
6849 views
use core::panic::AssertUnwindSafe;1use fixedbitset::FixedBitSet;23#[cfg(feature = "trace")]4use tracing::info_span;56#[cfg(feature = "std")]7use std::eprintln;89use crate::{10error::{ErrorContext, ErrorHandler},11schedule::{12is_apply_deferred, ConditionWithAccess, ExecutorKind, SystemExecutor, SystemSchedule,13},14system::{RunSystemError, ScheduleSystem},15world::World,16};1718#[cfg(feature = "hotpatching")]19use crate::{change_detection::DetectChanges, HotPatchChanges};2021use super::__rust_begin_short_backtrace;2223/// Runs the schedule using a single thread.24///25/// Useful if you're dealing with a single-threaded environment, saving your threads for26/// other things, or just trying minimize overhead.27#[derive(Default)]28pub struct SingleThreadedExecutor {29/// System sets whose conditions have been evaluated.30evaluated_sets: FixedBitSet,31/// Systems that have run or been skipped.32completed_systems: FixedBitSet,33/// Systems that have run but have not had their buffers applied.34unapplied_systems: FixedBitSet,35/// Setting when true applies deferred system buffers after all systems have run36apply_final_deferred: bool,37}3839impl SystemExecutor for SingleThreadedExecutor {40fn kind(&self) -> ExecutorKind {41ExecutorKind::SingleThreaded42}4344fn init(&mut self, schedule: &SystemSchedule) {45// pre-allocate space46let sys_count = schedule.system_ids.len();47let set_count = schedule.set_ids.len();48self.evaluated_sets = FixedBitSet::with_capacity(set_count);49self.completed_systems = FixedBitSet::with_capacity(sys_count);50self.unapplied_systems = FixedBitSet::with_capacity(sys_count);51}5253fn run(54&mut self,55schedule: &mut SystemSchedule,56world: &mut World,57_skip_systems: Option<&FixedBitSet>,58error_handler: ErrorHandler,59) {60// If stepping is enabled, make sure we skip those systems that should61// not be run.62#[cfg(feature = "bevy_debug_stepping")]63if let Some(skipped_systems) = _skip_systems {64// mark skipped systems as completed65self.completed_systems |= skipped_systems;66}6768#[cfg(feature = "hotpatching")]69let hotpatch_tick = world70.get_resource_ref::<HotPatchChanges>()71.map(|r| r.last_changed())72.unwrap_or_default();7374for system_index in 0..schedule.systems.len() {75let system = &mut schedule.systems[system_index].system;7677#[cfg(feature = "trace")]78let name = system.name();79#[cfg(feature = "trace")]80let should_run_span = info_span!("check_conditions", name = name.as_string()).entered();8182let mut should_run = !self.completed_systems.contains(system_index);83for set_idx in schedule.sets_with_conditions_of_systems[system_index].ones() {84if self.evaluated_sets.contains(set_idx) {85continue;86}8788// evaluate system set's conditions89let set_conditions_met = evaluate_and_fold_conditions(90&mut schedule.set_conditions[set_idx],91world,92error_handler,93system,94true,95);9697if !set_conditions_met {98self.completed_systems99.union_with(&schedule.systems_in_sets_with_conditions[set_idx]);100}101102should_run &= set_conditions_met;103self.evaluated_sets.insert(set_idx);104}105106// evaluate system's conditions107let system_conditions_met = evaluate_and_fold_conditions(108&mut schedule.system_conditions[system_index],109world,110error_handler,111system,112false,113);114115should_run &= system_conditions_met;116117#[cfg(feature = "trace")]118should_run_span.exit();119120#[cfg(feature = "hotpatching")]121if hotpatch_tick.is_newer_than(system.get_last_run(), world.change_tick()) {122system.refresh_hotpatch();123}124125// system has either been skipped or will run126self.completed_systems.insert(system_index);127128if !should_run {129continue;130}131132if is_apply_deferred(&**system) {133self.apply_deferred(schedule, world);134continue;135}136137let f = AssertUnwindSafe(|| {138if let Err(RunSystemError::Failed(err)) =139__rust_begin_short_backtrace::run_without_applying_deferred(system, world)140{141error_handler(142err,143ErrorContext::System {144name: system.name(),145last_run: system.get_last_run(),146},147);148}149});150151#[cfg(feature = "std")]152#[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")]153{154if let Err(payload) = std::panic::catch_unwind(f) {155eprintln!("Encountered a panic in system `{}`!", system.name());156std::panic::resume_unwind(payload);157}158}159160#[cfg(not(feature = "std"))]161{162(f)();163}164165self.unapplied_systems.insert(system_index);166}167168if self.apply_final_deferred {169self.apply_deferred(schedule, world);170}171self.evaluated_sets.clear();172self.completed_systems.clear();173}174175fn set_apply_final_deferred(&mut self, apply_final_deferred: bool) {176self.apply_final_deferred = apply_final_deferred;177}178}179180impl SingleThreadedExecutor {181/// Creates a new single-threaded executor for use in a [`Schedule`].182///183/// [`Schedule`]: crate::schedule::Schedule184pub const fn new() -> Self {185Self {186evaluated_sets: FixedBitSet::new(),187completed_systems: FixedBitSet::new(),188unapplied_systems: FixedBitSet::new(),189apply_final_deferred: true,190}191}192193fn apply_deferred(&mut self, schedule: &mut SystemSchedule, world: &mut World) {194for system_index in self.unapplied_systems.ones() {195let system = &mut schedule.systems[system_index].system;196system.apply_deferred(world);197}198199self.unapplied_systems.clear();200}201}202203fn evaluate_and_fold_conditions(204conditions: &mut [ConditionWithAccess],205world: &mut World,206error_handler: ErrorHandler,207for_system: &ScheduleSystem,208on_set: bool,209) -> bool {210#[cfg(feature = "hotpatching")]211let hotpatch_tick = world212.get_resource_ref::<HotPatchChanges>()213.map(|r| r.last_changed())214.unwrap_or_default();215216#[expect(217clippy::unnecessary_fold,218reason = "Short-circuiting here would prevent conditions from mutating their own state as needed."219)]220conditions221.iter_mut()222.map(|ConditionWithAccess { condition, .. }| {223#[cfg(feature = "hotpatching")]224if hotpatch_tick.is_newer_than(condition.get_last_run(), world.change_tick()) {225condition.refresh_hotpatch();226}227__rust_begin_short_backtrace::readonly_run(&mut **condition, world).unwrap_or_else(228|err| {229if let RunSystemError::Failed(err) = err {230error_handler(231err,232ErrorContext::RunCondition {233name: condition.name(),234last_run: condition.get_last_run(),235system: for_system.name(),236on_set,237},238);239};240false241},242)243})244.fold(true, |acc, res| acc && res)245}246247248