use std::collections::VecDeque;
use crate::driver::Installer;
use crate::system::{IntoSystem, System};
use crate::world::{Registry, World, WorldBuilder};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct SystemId(usize);
pub struct SchedulerInstaller {
systems: Vec<Box<dyn System>>,
edges: Vec<(usize, usize)>, }
impl SchedulerInstaller {
pub fn new() -> Self {
Self {
systems: Vec::new(),
edges: Vec::new(),
}
}
pub fn add<F, Params>(&mut self, f: F, registry: &Registry) -> SystemId
where
F: IntoSystem<Params>,
F::System: 'static,
{
let id = SystemId(self.systems.len());
self.systems.push(Box::new(f.into_system(registry)));
id
}
pub fn after(&mut self, downstream: SystemId, upstream: SystemId) {
self.edges.push((upstream.0, downstream.0));
}
pub fn before(&mut self, upstream: SystemId, downstream: SystemId) {
self.after(downstream, upstream);
}
}
impl Default for SchedulerInstaller {
fn default() -> Self {
Self::new()
}
}
pub const MAX_SYSTEMS: usize = 64;
impl Installer for SchedulerInstaller {
type Poller = SystemScheduler;
fn install(self, _world: &mut WorldBuilder) -> SystemScheduler {
let n = self.systems.len();
assert!(
n <= MAX_SYSTEMS,
"system scheduler supports at most {MAX_SYSTEMS} systems, got {n}",
);
let mut in_degree = vec![0usize; n];
let mut adj: Vec<Vec<usize>> = vec![Vec::new(); n];
for &(up, down) in &self.edges {
adj[up].push(down);
in_degree[down] += 1;
}
let mut queue = VecDeque::new();
for (i, deg) in in_degree.iter().enumerate() {
if *deg == 0 {
queue.push_back(i);
}
}
let mut order: Vec<usize> = Vec::with_capacity(n);
while let Some(node) = queue.pop_front() {
order.push(node);
for &succ in &adj[node] {
in_degree[succ] -= 1;
if in_degree[succ] == 0 {
queue.push_back(succ);
}
}
}
assert!(
order.len() == n,
"cycle detected in system scheduler: {} systems in graph, \
but only {} reachable in topological order",
n,
order.len(),
);
let mut old_to_new = vec![0usize; n];
for (new_pos, &old_pos) in order.iter().enumerate() {
old_to_new[old_pos] = new_pos;
}
let mut sorted_systems: Vec<Option<Box<dyn System>>> =
self.systems.into_iter().map(Some).collect();
let systems: Vec<Box<dyn System>> = order
.iter()
.map(|&old_pos| sorted_systems[old_pos].take().unwrap())
.collect();
let mut upstream_masks = vec![0u64; n];
for &(up, down) in &self.edges {
upstream_masks[old_to_new[down]] |= 1 << old_to_new[up];
}
SystemScheduler {
systems,
upstream_masks,
}
}
}
pub struct SystemScheduler {
systems: Vec<Box<dyn System>>,
upstream_masks: Vec<u64>,
}
impl SystemScheduler {
pub fn run(&mut self, world: &mut World) -> usize {
let mut ran = 0;
let mut results: u64 = 0;
for i in 0..self.systems.len() {
let mask = self.upstream_masks[i];
if mask == 0 || (mask & results) != 0 {
if self.systems[i].run(world) {
results |= 1 << i;
}
ran += 1;
}
}
ran
}
pub fn len(&self) -> usize {
self.systems.len()
}
pub fn is_empty(&self) -> bool {
self.systems.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ResMut;
#[test]
fn empty_scheduler() {
let mut builder = WorldBuilder::new();
let installer = SchedulerInstaller::new();
let mut scheduler = builder.install_driver(installer);
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 0);
assert!(scheduler.is_empty());
}
fn increment(mut val: ResMut<u64>) -> bool {
*val += 1;
true
}
#[test]
fn single_root_always_runs() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut installer = SchedulerInstaller::new();
installer.add(increment, builder.registry());
let mut scheduler = builder.install_driver(installer);
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 1);
assert_eq!(*world.resource::<u64>(), 1);
}
fn source(mut val: ResMut<u64>) -> bool {
*val += 1;
*val <= 2 }
fn middle(mut val: ResMut<u64>) -> bool {
*val += 10;
true
}
fn leaf(mut val: ResMut<u64>) -> bool {
*val += 100;
true
}
#[test]
fn linear_chain_propagation() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut installer = SchedulerInstaller::new();
let a = installer.add(source, builder.registry());
let b = installer.add(middle, builder.registry());
let c = installer.add(leaf, builder.registry());
installer.after(b, a);
installer.after(c, b);
let mut scheduler = builder.install_driver(installer);
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 3);
assert_eq!(*world.resource::<u64>(), 111);
}
fn false_source() -> bool {
false
}
fn should_not_run(mut val: ResMut<u64>) -> bool {
*val = 999;
true
}
#[test]
fn propagation_stops_on_false() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut installer = SchedulerInstaller::new();
let a = installer.add(false_source, builder.registry());
let b = installer.add(should_not_run, builder.registry());
installer.after(b, a);
let mut scheduler = builder.install_driver(installer);
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 1);
assert_eq!(*world.resource::<u64>(), 0);
}
fn set_flag(mut flag: ResMut<bool>) -> bool {
*flag = true;
true
}
#[test]
fn diamond_dag() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
builder.register::<bool>(false);
let mut installer = SchedulerInstaller::new();
let a = installer.add(increment, builder.registry());
let b = installer.add(increment, builder.registry());
let c = installer.add(set_flag, builder.registry());
let d = installer.add(increment, builder.registry());
installer.after(b, a);
installer.after(c, a);
installer.after(d, b);
installer.after(d, c);
let mut scheduler = builder.install_driver(installer);
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 4);
assert!(*world.resource::<bool>());
assert_eq!(*world.resource::<u64>(), 3);
}
#[test]
fn multiple_roots() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut installer = SchedulerInstaller::new();
installer.add(increment, builder.registry());
installer.add(increment, builder.registry());
installer.add(increment, builder.registry());
let mut scheduler = builder.install_driver(installer);
let mut world = builder.build();
assert_eq!(scheduler.run(&mut world), 3);
assert_eq!(*world.resource::<u64>(), 3);
}
#[test]
#[should_panic(expected = "cycle detected")]
fn cycle_panics() {
let mut builder = WorldBuilder::new();
let mut installer = SchedulerInstaller::new();
let a = installer.add(false_source, builder.registry());
let b = installer.add(false_source, builder.registry());
installer.after(b, a);
installer.after(a, b);
let _scheduler = builder.install_driver(installer);
}
#[test]
fn scheduler_does_not_bump_sequence() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut installer = SchedulerInstaller::new();
installer.add(increment, builder.registry());
let mut scheduler = builder.install_driver(installer);
let mut world = builder.build();
let before = world.current_sequence();
scheduler.run(&mut world);
assert_eq!(world.current_sequence(), before);
}
fn double(mut val: ResMut<u64>) -> bool {
*val *= 2;
true
}
#[test]
fn mutations_visible_downstream() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(1);
let mut installer = SchedulerInstaller::new();
let a = installer.add(double, builder.registry());
let b = installer.add(double, builder.registry());
installer.after(b, a);
let mut scheduler = builder.install_driver(installer);
let mut world = builder.build();
scheduler.run(&mut world);
assert_eq!(*world.resource::<u64>(), 4);
}
#[test]
#[should_panic(expected = "at most 64 systems")]
fn exceeding_max_systems_panics() {
let mut builder = WorldBuilder::new();
let mut installer = SchedulerInstaller::new();
for _ in 0..=MAX_SYSTEMS {
installer.add(false_source, builder.registry());
}
let _scheduler = builder.install_driver(installer);
}
}