Skip to content

Commit 9eba19c

Browse files
authored
Fix for bug #449 (#450)
Fix for a bug (#449) in scheduler that could result in systems running concurrently when they shouldn't.
1 parent 8b35530 commit 9eba19c

File tree

2 files changed

+60
-2
lines changed

2 files changed

+60
-2
lines changed

crates/bevy_ecs/src/schedule/parallel_executor.rs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,11 @@ impl ExecutorStage {
144144
for system_index in prepare_system_index_range.clone() {
145145
let mut system = systems[system_index].lock();
146146
system.update_archetype_access(world);
147+
148+
// Clear this so that the next block of code that populates it doesn't insert
149+
// duplicates
150+
self.system_dependents[system_index].clear();
151+
self.system_dependencies[system_index].clear();
147152
}
148153

149154
// calculate dependencies between systems and build execution order
@@ -204,6 +209,27 @@ impl ExecutorStage {
204209
}
205210
}
206211

212+
// Verify that dependents are not duplicated
213+
#[cfg(debug_assertions)]
214+
for system_index in prepare_system_index_range.clone() {
215+
let mut system_dependents_set = std::collections::HashSet::new();
216+
for dependent_system in &self.system_dependents[system_index] {
217+
let inserted = system_dependents_set.insert(*dependent_system);
218+
219+
// This means duplicate values are in the system_dependents list
220+
// This is reproducing when archetypes change. When we fix this, we can remove
221+
// the hack below and make this a debug-only assert or remove it
222+
debug_assert!(inserted);
223+
}
224+
}
225+
226+
// Clear the ready events lists associated with each system so we can rebuild them
227+
for ready_events_of_dependents in
228+
&mut self.ready_events_of_dependents[prepare_system_index_range.clone()]
229+
{
230+
ready_events_of_dependents.clear();
231+
}
232+
207233
// Now that system_dependents and system_dependencies is populated, update
208234
// system_dependency_count and ready_events
209235
for system_index in prepare_system_index_range.clone() {
@@ -285,7 +311,11 @@ impl ExecutorStage {
285311
);
286312

287313
for dependency in self.system_dependencies[system_index].ones() {
288-
log::trace!(" * Depends on {}", systems[dependency].lock().name());
314+
log::trace!(
315+
" * system ({}) depends on {}",
316+
system_index,
317+
systems[dependency].lock().name()
318+
);
289319
}
290320

291321
// This event will be awaited, preventing the task from starting until all
@@ -298,7 +328,6 @@ impl ExecutorStage {
298328
if start_system_index != 0 {
299329
if let Some(ready_event) = ready_event.as_ref() {
300330
for dependency in self.system_dependencies[system_index].ones() {
301-
log::trace!(" * Depends on {}", dependency);
302331
if dependency < start_system_index {
303332
ready_event.decrement();
304333
}
@@ -309,8 +338,32 @@ impl ExecutorStage {
309338
let world_ref = &*world;
310339
let resources_ref = &*resources;
311340

341+
let dependent_systems = &self.system_dependents[system_index];
312342
let trigger_events = &self.ready_events_of_dependents[system_index];
313343

344+
// Verify that any dependent task has a > 0 count. If a dependent task has > 0
345+
// count, then the current system we are starting now isn't blocking it from running
346+
// as it should be. Failure here implies the sync primitives are not matching the
347+
// intended schedule. This likely compiles out if trace/asserts are disabled but
348+
// make it explicitly debug-only anyways
349+
#[cfg(debug_assertions)]
350+
{
351+
debug_assert_eq!(trigger_events.len(), dependent_systems.len());
352+
for (trigger_event, dependent_system_index) in
353+
trigger_events.iter().zip(dependent_systems)
354+
{
355+
log::trace!(
356+
" * system ({}) triggers events: ({}): {}",
357+
system_index,
358+
dependent_system_index,
359+
trigger_event.get()
360+
);
361+
debug_assert!(
362+
*dependent_system_index < start_system_index || trigger_event.get() > 0
363+
);
364+
}
365+
}
366+
314367
// Spawn the task
315368
scope.spawn(async move {
316369
// Wait until our dependencies are done

crates/bevy_tasks/src/countdown_event.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ impl CountdownEvent {
3434
}
3535
}
3636

37+
/// Get the number of times decrement must be called to trigger notifying all listeners
38+
pub fn get(&self) -> isize {
39+
self.inner.counter.load(Ordering::Acquire)
40+
}
41+
3742
/// Decrement the counter by one. If this is the Nth call, trigger all listeners
3843
pub fn decrement(&self) {
3944
// If we are the last decrementer, notify listeners

0 commit comments

Comments
 (0)