bevy_ecs/schedule/executor/
simple.rs1#![expect(deprecated, reason = "Everything here is deprecated")]
2
3use core::panic::AssertUnwindSafe;
4use fixedbitset::FixedBitSet;
5
6#[cfg(feature = "trace")]
7use tracing::info_span;
8
9#[cfg(feature = "std")]
10use std::eprintln;
11
12#[cfg(feature = "hotpatching")]
13use crate::{change_detection::DetectChanges, HotPatchChanges};
14use crate::{
15 error::{ErrorContext, ErrorHandler},
16 schedule::{
17 executor::is_apply_deferred, ConditionWithAccess, ExecutorKind, SystemExecutor,
18 SystemSchedule,
19 },
20 system::{RunSystemError, ScheduleSystem},
21 world::World,
22};
23
24use super::__rust_begin_short_backtrace;
25
26#[derive(Default)]
29#[deprecated(
30 since = "0.17.0",
31 note = "Use SingleThreadedExecutor instead. See https://github.com/bevyengine/bevy/issues/18453 for motivation."
32)]
33pub struct SimpleExecutor {
34 evaluated_sets: FixedBitSet,
36 completed_systems: FixedBitSet,
38}
39
40impl SystemExecutor for SimpleExecutor {
41 fn kind(&self) -> ExecutorKind {
42 ExecutorKind::Simple
43 }
44
45 fn init(&mut self, schedule: &SystemSchedule) {
46 let sys_count = schedule.system_ids.len();
47 let set_count = schedule.set_ids.len();
48 self.evaluated_sets = FixedBitSet::with_capacity(set_count);
49 self.completed_systems = FixedBitSet::with_capacity(sys_count);
50 }
51
52 fn run(
53 &mut self,
54 schedule: &mut SystemSchedule,
55 world: &mut World,
56 _skip_systems: Option<&FixedBitSet>,
57 error_handler: ErrorHandler,
58 ) {
59 #[cfg(feature = "bevy_debug_stepping")]
62 if let Some(skipped_systems) = _skip_systems {
63 self.completed_systems |= skipped_systems;
65 }
66
67 #[cfg(feature = "hotpatching")]
68 let hotpatch_tick = world
69 .get_resource_ref::<HotPatchChanges>()
70 .map(|r| r.last_changed())
71 .unwrap_or_default();
72
73 for system_index in 0..schedule.systems.len() {
74 let system = &mut schedule.systems[system_index].system;
75
76 #[cfg(feature = "trace")]
77 let name = system.name();
78 #[cfg(feature = "trace")]
79 let should_run_span = info_span!("check_conditions", name = name.as_string()).entered();
80
81 let mut should_run = !self.completed_systems.contains(system_index);
82 for set_idx in schedule.sets_with_conditions_of_systems[system_index].ones() {
83 if self.evaluated_sets.contains(set_idx) {
84 continue;
85 }
86
87 let set_conditions_met = evaluate_and_fold_conditions(
89 &mut schedule.set_conditions[set_idx],
90 world,
91 error_handler,
92 system,
93 true,
94 );
95
96 if !set_conditions_met {
97 self.completed_systems
98 .union_with(&schedule.systems_in_sets_with_conditions[set_idx]);
99 }
100
101 should_run &= set_conditions_met;
102 self.evaluated_sets.insert(set_idx);
103 }
104
105 let system_conditions_met = evaluate_and_fold_conditions(
107 &mut schedule.system_conditions[system_index],
108 world,
109 error_handler,
110 system,
111 false,
112 );
113
114 should_run &= system_conditions_met;
115
116 #[cfg(feature = "trace")]
117 should_run_span.exit();
118
119 #[cfg(feature = "hotpatching")]
120 if hotpatch_tick.is_newer_than(system.get_last_run(), world.change_tick()) {
121 system.refresh_hotpatch();
122 }
123
124 self.completed_systems.insert(system_index);
126
127 if !should_run {
128 continue;
129 }
130
131 if is_apply_deferred(&**system) {
132 continue;
133 }
134
135 let f = AssertUnwindSafe(|| {
136 if let Err(RunSystemError::Failed(err)) =
137 __rust_begin_short_backtrace::run(system, world)
138 {
139 error_handler(
140 err,
141 ErrorContext::System {
142 name: system.name(),
143 last_run: system.get_last_run(),
144 },
145 );
146 }
147 });
148
149 #[cfg(feature = "std")]
150 #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")]
151 {
152 if let Err(payload) = std::panic::catch_unwind(f) {
153 eprintln!("Encountered a panic in system `{}`!", system.name());
154 std::panic::resume_unwind(payload);
155 }
156 }
157
158 #[cfg(not(feature = "std"))]
159 {
160 (f)();
161 }
162 }
163
164 self.evaluated_sets.clear();
165 self.completed_systems.clear();
166 }
167
168 fn set_apply_final_deferred(&mut self, _: bool) {
169 }
171}
172
173impl SimpleExecutor {
174 pub const fn new() -> Self {
177 Self {
178 evaluated_sets: FixedBitSet::new(),
179 completed_systems: FixedBitSet::new(),
180 }
181 }
182}
183#[deprecated(
184 since = "0.17.0",
185 note = "Use SingleThreadedExecutor instead. See https://github.com/bevyengine/bevy/issues/18453 for motivation."
186)]
187fn evaluate_and_fold_conditions(
188 conditions: &mut [ConditionWithAccess],
189 world: &mut World,
190 error_handler: ErrorHandler,
191 for_system: &ScheduleSystem,
192 on_set: bool,
193) -> bool {
194 #[cfg(feature = "hotpatching")]
195 let hotpatch_tick = world
196 .get_resource_ref::<HotPatchChanges>()
197 .map(|r| r.last_changed())
198 .unwrap_or_default();
199
200 #[expect(
201 clippy::unnecessary_fold,
202 reason = "Short-circuiting here would prevent conditions from mutating their own state as needed."
203 )]
204 conditions
205 .iter_mut()
206 .map(|ConditionWithAccess { condition, .. }| {
207 #[cfg(feature = "hotpatching")]
208 if hotpatch_tick.is_newer_than(condition.get_last_run(), world.change_tick()) {
209 condition.refresh_hotpatch();
210 }
211 __rust_begin_short_backtrace::readonly_run(&mut **condition, world).unwrap_or_else(
212 |err| {
213 if let RunSystemError::Failed(err) = err {
214 error_handler(
215 err,
216 ErrorContext::RunCondition {
217 name: condition.name(),
218 last_run: condition.get_last_run(),
219 system: for_system.name(),
220 on_set,
221 },
222 );
223 };
224 false
225 },
226 )
227 })
228 .fold(true, |acc, res| acc && res)
229}
230
231#[cfg(test)]
232#[test]
233fn skip_automatic_sync_points() {
234 use crate::prelude::*;
237 let mut sched = Schedule::default();
238 sched.set_executor_kind(ExecutorKind::Simple);
239 sched.add_systems((|_: Commands| (), || ()).chain());
240 let mut world = World::new();
241 sched.run(&mut world);
242}