Skip to content

Commit 0ee9377

Browse files
authored
Simplify self-edge checking in schedule building (#20015)
# Objective Make the schedule graph code more understandable, and replace some panics with `Result`s. I found the `check_edges` and `check_hierarchy` functions [a little confusing](#19352 (comment)), as they combined two concerns: Initializing graph nodes for system sets, and checking for self-edges on system sets. It was hard to understand the self-edge checks because it wasn't clear what `NodeId` was being checked against! So, let's separate those concerns, and move them to more appropriate locations. Fix a bug where `schedule.configure_sets((SameSet, SameSet).chain());` would panic with an unhelpful message: `assertion failed: index_a < index_b`. ## Solution Remove the `check_edges` and `check_hierarchy` functions, separating the initialization behavior and the checking behavior and moving them where they are easier to understand. For initializing graph nodes, do this on-demand using the `entry` API by replacing later `self.system_set_ids[&set]` calls with a `self.system_sets.get_or_add_set(set)` method. This should avoid the need for an extra pass over the graph and an extra lookup. Unfortunately, implementing that method directly on `ScheduleGraph` leads to borrowing errors as it borrows the entire `struct`. So, split out the collections managing system sets into a separate `struct`. For checking self-edges, move this check later so that it can be reported by returning a `Result` from `Schedule::initialize` instead of having to panic in `configure_set_inner`. The issue was that `iter_sccs` does not report self-edges as cycles, since the resulting components only have one node, but that later code assumes all edges point forward. So, check for self-edges directly, immediately before calling `iter_sccs`. This also ensures we catch *every* way that self-edges can be added. The previous code missed an edge case where `chain()`ing a set to itself would create a self-edge and would trigger a `debug_assert`.
1 parent 9bb41a8 commit 0ee9377

File tree

2 files changed

+88
-131
lines changed

2 files changed

+88
-131
lines changed

crates/bevy_ecs/src/schedule/mod.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -563,10 +563,21 @@ mod tests {
563563
use super::*;
564564

565565
#[test]
566-
#[should_panic]
567566
fn dependency_loop() {
568567
let mut schedule = Schedule::default();
569568
schedule.configure_sets(TestSystems::X.after(TestSystems::X));
569+
let mut world = World::new();
570+
let result = schedule.initialize(&mut world);
571+
assert!(matches!(result, Err(ScheduleBuildError::DependencyLoop(_))));
572+
}
573+
574+
#[test]
575+
fn dependency_loop_from_chain() {
576+
let mut schedule = Schedule::default();
577+
schedule.configure_sets((TestSystems::X, TestSystems::X).chain());
578+
let mut world = World::new();
579+
let result = schedule.initialize(&mut world);
580+
assert!(matches!(result, Err(ScheduleBuildError::DependencyLoop(_))));
570581
}
571582

572583
#[test]
@@ -598,10 +609,12 @@ mod tests {
598609
}
599610

600611
#[test]
601-
#[should_panic]
602612
fn hierarchy_loop() {
603613
let mut schedule = Schedule::default();
604614
schedule.configure_sets(TestSystems::X.in_set(TestSystems::X));
615+
let mut world = World::new();
616+
let result = schedule.initialize(&mut world);
617+
assert!(matches!(result, Err(ScheduleBuildError::HierarchyLoop(_))));
605618
}
606619

607620
#[test]

0 commit comments

Comments
 (0)