bevy_ecs/schedule/executor/
single_threaded.rs

1#[cfg(feature = "trace")]
2use bevy_utils::tracing::info_span;
3use core::panic::AssertUnwindSafe;
4use fixedbitset::FixedBitSet;
5
6use crate::{
7    schedule::{is_apply_deferred, BoxedCondition, ExecutorKind, SystemExecutor, SystemSchedule},
8    world::World,
9};
10
11use super::__rust_begin_short_backtrace;
12
13/// Runs the schedule using a single thread.
14///
15/// Useful if you're dealing with a single-threaded environment, saving your threads for
16/// other things, or just trying minimize overhead.
17#[derive(Default)]
18pub struct SingleThreadedExecutor {
19    /// System sets whose conditions have been evaluated.
20    evaluated_sets: FixedBitSet,
21    /// Systems that have run or been skipped.
22    completed_systems: FixedBitSet,
23    /// Systems that have run but have not had their buffers applied.
24    unapplied_systems: FixedBitSet,
25    /// Setting when true applies deferred system buffers after all systems have run
26    apply_final_deferred: bool,
27}
28
29impl SystemExecutor for SingleThreadedExecutor {
30    fn kind(&self) -> ExecutorKind {
31        ExecutorKind::SingleThreaded
32    }
33
34    fn init(&mut self, schedule: &SystemSchedule) {
35        // pre-allocate space
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        self.unapplied_systems = FixedBitSet::with_capacity(sys_count);
41    }
42
43    fn run(
44        &mut self,
45        schedule: &mut SystemSchedule,
46        world: &mut World,
47        _skip_systems: Option<&FixedBitSet>,
48    ) {
49        // If stepping is enabled, make sure we skip those systems that should
50        // not be run.
51        #[cfg(feature = "bevy_debug_stepping")]
52        if let Some(skipped_systems) = _skip_systems {
53            // mark skipped systems as completed
54            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                // evaluate system set's conditions
70                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            // evaluate system's conditions
83            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 = system.validate_param(world);
91                should_run &= valid_params;
92            }
93
94            #[cfg(feature = "trace")]
95            should_run_span.exit();
96
97            // system has either been skipped or will run
98            self.completed_systems.insert(system_index);
99
100            if !should_run {
101                continue;
102            }
103
104            if is_apply_deferred(system) {
105                self.apply_deferred(schedule, world);
106                continue;
107            }
108
109            let res = std::panic::catch_unwind(AssertUnwindSafe(|| {
110                if system.is_exclusive() {
111                    __rust_begin_short_backtrace::run(&mut **system, world);
112                } else {
113                    // Use run_unsafe to avoid immediately applying deferred buffers
114                    let world = world.as_unsafe_world_cell();
115                    system.update_archetype_component_access(world);
116                    // SAFETY: We have exclusive, single-threaded access to the world and
117                    // update_archetype_component_access is being called immediately before this.
118                    unsafe { __rust_begin_short_backtrace::run_unsafe(&mut **system, world) };
119                }
120            }));
121            if let Err(payload) = res {
122                eprintln!("Encountered a panic in system `{}`!", &*system.name());
123                std::panic::resume_unwind(payload);
124            }
125            self.unapplied_systems.insert(system_index);
126        }
127
128        if self.apply_final_deferred {
129            self.apply_deferred(schedule, world);
130        }
131        self.evaluated_sets.clear();
132        self.completed_systems.clear();
133    }
134
135    fn set_apply_final_deferred(&mut self, apply_final_deferred: bool) {
136        self.apply_final_deferred = apply_final_deferred;
137    }
138}
139
140impl SingleThreadedExecutor {
141    /// Creates a new single-threaded executor for use in a [`Schedule`].
142    ///
143    /// [`Schedule`]: crate::schedule::Schedule
144    pub const fn new() -> Self {
145        Self {
146            evaluated_sets: FixedBitSet::new(),
147            completed_systems: FixedBitSet::new(),
148            unapplied_systems: FixedBitSet::new(),
149            apply_final_deferred: true,
150        }
151    }
152
153    fn apply_deferred(&mut self, schedule: &mut SystemSchedule, world: &mut World) {
154        for system_index in self.unapplied_systems.ones() {
155            let system = &mut schedule.systems[system_index];
156            system.apply_deferred(world);
157        }
158
159        self.unapplied_systems.clear();
160    }
161}
162
163fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &mut World) -> bool {
164    // not short-circuiting is intentional
165    #[allow(clippy::unnecessary_fold)]
166    conditions
167        .iter_mut()
168        .map(|condition| {
169            if !condition.validate_param(world) {
170                return false;
171            }
172            __rust_begin_short_backtrace::readonly_run(&mut **condition, world)
173        })
174        .fold(true, |acc, res| acc && res)
175}