bevy_ecs/schedule/executor/
simple.rs1use core::panic::AssertUnwindSafe;
2use fixedbitset::FixedBitSet;
3
4#[cfg(feature = "trace")]
5use tracing::info_span;
6
7#[cfg(feature = "std")]
8use std::eprintln;
9
10use crate::{
11 error::{default_error_handler, BevyError, ErrorContext},
12 schedule::{
13 executor::is_apply_deferred, BoxedCondition, ExecutorKind, SystemExecutor, SystemSchedule,
14 },
15 world::World,
16};
17
18use super::__rust_begin_short_backtrace;
19
20#[derive(Default)]
23pub struct SimpleExecutor {
24 evaluated_sets: FixedBitSet,
26 completed_systems: FixedBitSet,
28}
29
30impl SystemExecutor for SimpleExecutor {
31 fn kind(&self) -> ExecutorKind {
32 ExecutorKind::Simple
33 }
34
35 fn init(&mut self, schedule: &SystemSchedule) {
36 let sys_count = schedule.system_ids.len();
37 let set_count = schedule.set_ids.len();
38 self.evaluated_sets = FixedBitSet::with_capacity(set_count);
39 self.completed_systems = FixedBitSet::with_capacity(sys_count);
40 }
41
42 fn run(
43 &mut self,
44 schedule: &mut SystemSchedule,
45 world: &mut World,
46 _skip_systems: Option<&FixedBitSet>,
47 error_handler: fn(BevyError, ErrorContext),
48 ) {
49 #[cfg(feature = "bevy_debug_stepping")]
52 if let Some(skipped_systems) = _skip_systems {
53 self.completed_systems |= skipped_systems;
55 }
56
57 for system_index in 0..schedule.systems.len() {
58 #[cfg(feature = "trace")]
59 let name = schedule.systems[system_index].name();
60 #[cfg(feature = "trace")]
61 let should_run_span = info_span!("check_conditions", name = &*name).entered();
62
63 let mut should_run = !self.completed_systems.contains(system_index);
64 for set_idx in schedule.sets_with_conditions_of_systems[system_index].ones() {
65 if self.evaluated_sets.contains(set_idx) {
66 continue;
67 }
68
69 let set_conditions_met =
71 evaluate_and_fold_conditions(&mut schedule.set_conditions[set_idx], world);
72
73 if !set_conditions_met {
74 self.completed_systems
75 .union_with(&schedule.systems_in_sets_with_conditions[set_idx]);
76 }
77
78 should_run &= set_conditions_met;
79 self.evaluated_sets.insert(set_idx);
80 }
81
82 let system_conditions_met =
84 evaluate_and_fold_conditions(&mut schedule.system_conditions[system_index], world);
85
86 should_run &= system_conditions_met;
87
88 let system = &mut schedule.systems[system_index];
89 if should_run {
90 let valid_params = match system.validate_param(world) {
91 Ok(()) => true,
92 Err(e) => {
93 if !e.skipped {
94 error_handler(
95 e.into(),
96 ErrorContext::System {
97 name: system.name(),
98 last_run: system.get_last_run(),
99 },
100 );
101 }
102 false
103 }
104 };
105 should_run &= valid_params;
106 }
107
108 #[cfg(feature = "trace")]
109 should_run_span.exit();
110
111 self.completed_systems.insert(system_index);
113
114 if !should_run {
115 continue;
116 }
117
118 if is_apply_deferred(system) {
119 continue;
120 }
121
122 let f = AssertUnwindSafe(|| {
123 if let Err(err) = __rust_begin_short_backtrace::run(system, world) {
124 error_handler(
125 err,
126 ErrorContext::System {
127 name: system.name(),
128 last_run: system.get_last_run(),
129 },
130 );
131 }
132 });
133
134 #[cfg(feature = "std")]
135 #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")]
136 {
137 if let Err(payload) = std::panic::catch_unwind(f) {
138 eprintln!("Encountered a panic in system `{}`!", &*system.name());
139 std::panic::resume_unwind(payload);
140 }
141 }
142
143 #[cfg(not(feature = "std"))]
144 {
145 (f)();
146 }
147 }
148
149 self.evaluated_sets.clear();
150 self.completed_systems.clear();
151 }
152
153 fn set_apply_final_deferred(&mut self, _: bool) {
154 }
156}
157
158impl SimpleExecutor {
159 pub const fn new() -> Self {
162 Self {
163 evaluated_sets: FixedBitSet::new(),
164 completed_systems: FixedBitSet::new(),
165 }
166 }
167}
168
169fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &mut World) -> bool {
170 let error_handler = default_error_handler();
171
172 #[expect(
173 clippy::unnecessary_fold,
174 reason = "Short-circuiting here would prevent conditions from mutating their own state as needed."
175 )]
176 conditions
177 .iter_mut()
178 .map(|condition| {
179 match condition.validate_param(world) {
180 Ok(()) => (),
181 Err(e) => {
182 if !e.skipped {
183 error_handler(
184 e.into(),
185 ErrorContext::System {
186 name: condition.name(),
187 last_run: condition.get_last_run(),
188 },
189 );
190 }
191 return false;
192 }
193 }
194 __rust_begin_short_backtrace::readonly_run(&mut **condition, world)
195 })
196 .fold(true, |acc, res| acc && res)
197}
198
199#[cfg(test)]
200#[test]
201fn skip_automatic_sync_points() {
202 use crate::prelude::*;
205 let mut sched = Schedule::default();
206 sched.set_executor_kind(ExecutorKind::Simple);
207 sched.add_systems((|_: Commands| (), || ()).chain());
208 let mut world = World::new();
209 sched.run(&mut world);
210}