Path: blob/main/crates/bevy_ecs/src/schedule/executor/single_threaded.rs
9351 views
use core::panic::AssertUnwindSafe;1use fixedbitset::FixedBitSet;23#[cfg(feature = "trace")]4use alloc::string::ToString as _;5#[cfg(feature = "trace")]6use tracing::info_span;78#[cfg(feature = "std")]9use std::eprintln;1011use crate::{12error::{ErrorContext, ErrorHandler},13schedule::{14is_apply_deferred, ConditionWithAccess, ExecutorKind, SystemExecutor, SystemSchedule,15},16system::{RunSystemError, ScheduleSystem},17world::World,18};1920#[cfg(feature = "hotpatching")]21use crate::{change_detection::DetectChanges, HotPatchChanges};2223use super::__rust_begin_short_backtrace;2425/// Runs the schedule using a single thread.26///27/// Useful if you're dealing with a single-threaded environment, saving your threads for28/// other things, or just trying minimize overhead.29#[derive(Default)]30pub struct SingleThreadedExecutor {31/// System sets whose conditions have been evaluated.32evaluated_sets: FixedBitSet,33/// Systems that have run or been skipped.34completed_systems: FixedBitSet,35/// Systems that have run but have not had their buffers applied.36unapplied_systems: FixedBitSet,37/// Setting when true applies deferred system buffers after all systems have run38apply_final_deferred: bool,39}4041impl SystemExecutor for SingleThreadedExecutor {42fn kind(&self) -> ExecutorKind {43ExecutorKind::SingleThreaded44}4546fn init(&mut self, schedule: &SystemSchedule) {47// pre-allocate space48let sys_count = schedule.system_ids.len();49let set_count = schedule.set_ids.len();50self.evaluated_sets = FixedBitSet::with_capacity(set_count);51self.completed_systems = FixedBitSet::with_capacity(sys_count);52self.unapplied_systems = FixedBitSet::with_capacity(sys_count);53}5455fn run(56&mut self,57schedule: &mut SystemSchedule,58world: &mut World,59_skip_systems: Option<&FixedBitSet>,60error_handler: ErrorHandler,61) {62// If stepping is enabled, make sure we skip those systems that should63// not be run.64#[cfg(feature = "bevy_debug_stepping")]65if let Some(skipped_systems) = _skip_systems {66// mark skipped systems as completed67self.completed_systems |= skipped_systems;68}6970#[cfg(feature = "hotpatching")]71let hotpatch_tick = world72.get_resource_ref::<HotPatchChanges>()73.map(|r| r.last_changed())74.unwrap_or_default();7576for system_index in 0..schedule.systems.len() {77let system = &mut schedule.systems[system_index].system;7879#[cfg(feature = "trace")]80let name = system.name();81#[cfg(feature = "trace")]82let should_run_span = info_span!("check_conditions", name = name.to_string()).entered();8384let mut should_run = !self.completed_systems.contains(system_index);85for set_idx in schedule.sets_with_conditions_of_systems[system_index].ones() {86if self.evaluated_sets.contains(set_idx) {87continue;88}8990// evaluate system set's conditions91let set_conditions_met = evaluate_and_fold_conditions(92&mut schedule.set_conditions[set_idx],93world,94error_handler,95system,96true,97);9899if !set_conditions_met {100self.completed_systems101.union_with(&schedule.systems_in_sets_with_conditions[set_idx]);102}103104should_run &= set_conditions_met;105self.evaluated_sets.insert(set_idx);106}107108// evaluate system's conditions109let system_conditions_met = evaluate_and_fold_conditions(110&mut schedule.system_conditions[system_index],111world,112error_handler,113system,114false,115);116117should_run &= system_conditions_met;118119#[cfg(feature = "trace")]120should_run_span.exit();121122#[cfg(feature = "hotpatching")]123if hotpatch_tick.is_newer_than(system.get_last_run(), world.change_tick()) {124system.refresh_hotpatch();125}126127// system has either been skipped or will run128self.completed_systems.insert(system_index);129130if !should_run {131continue;132}133134if is_apply_deferred(&**system) {135self.apply_deferred(schedule, world);136continue;137}138139let f = AssertUnwindSafe(|| {140if let Err(RunSystemError::Failed(err)) =141__rust_begin_short_backtrace::run_without_applying_deferred(system, world)142{143error_handler(144err,145ErrorContext::System {146name: system.name(),147last_run: system.get_last_run(),148},149);150}151});152153#[cfg(feature = "std")]154#[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")]155{156if let Err(payload) = std::panic::catch_unwind(f) {157eprintln!("Encountered a panic in system `{}`!", system.name());158std::panic::resume_unwind(payload);159}160}161162#[cfg(not(feature = "std"))]163{164(f)();165}166167self.unapplied_systems.insert(system_index);168}169170if self.apply_final_deferred {171self.apply_deferred(schedule, world);172}173self.evaluated_sets.clear();174self.completed_systems.clear();175}176177fn set_apply_final_deferred(&mut self, apply_final_deferred: bool) {178self.apply_final_deferred = apply_final_deferred;179}180}181182impl SingleThreadedExecutor {183/// Creates a new single-threaded executor for use in a [`Schedule`].184///185/// [`Schedule`]: crate::schedule::Schedule186pub const fn new() -> Self {187Self {188evaluated_sets: FixedBitSet::new(),189completed_systems: FixedBitSet::new(),190unapplied_systems: FixedBitSet::new(),191apply_final_deferred: true,192}193}194195fn apply_deferred(&mut self, schedule: &mut SystemSchedule, world: &mut World) {196for system_index in self.unapplied_systems.ones() {197let system = &mut schedule.systems[system_index].system;198system.apply_deferred(world);199}200201self.unapplied_systems.clear();202}203}204205fn evaluate_and_fold_conditions(206conditions: &mut [ConditionWithAccess],207world: &mut World,208error_handler: ErrorHandler,209for_system: &ScheduleSystem,210on_set: bool,211) -> bool {212#[cfg(feature = "hotpatching")]213let hotpatch_tick = world214.get_resource_ref::<HotPatchChanges>()215.map(|r| r.last_changed())216.unwrap_or_default();217218#[expect(219clippy::unnecessary_fold,220reason = "Short-circuiting here would prevent conditions from mutating their own state as needed."221)]222conditions223.iter_mut()224.map(|ConditionWithAccess { condition, .. }| {225#[cfg(feature = "hotpatching")]226if hotpatch_tick.is_newer_than(condition.get_last_run(), world.change_tick()) {227condition.refresh_hotpatch();228}229__rust_begin_short_backtrace::readonly_run(&mut **condition, world).unwrap_or_else(230|err| {231if let RunSystemError::Failed(err) = err {232error_handler(233err,234ErrorContext::RunCondition {235name: condition.name(),236last_run: condition.get_last_run(),237system: for_system.name(),238on_set,239},240);241};242false243},244)245})246.fold(true, |acc, res| acc && res)247}248249250