mod components;
mod graph_builder;
mod manager;
mod runner;
pub use components::{
add_prong, finalize, join, make_fork, make_task, with_task_components, FinalTag, OnCompletion,
TaskComponent, TaskProgress,
};
pub use graph_builder::{Cons, TaskFactory, TaskGraph};
pub use manager::{build_task_manager_system, entity_is_complete};
pub use runner::{run_tasks, task_runner_query, TaskEntityFilter, TaskQuery, TaskSystemQuery};
#[cfg(test)]
mod tests {
use super::*;
use legion::prelude::*;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct Noop {
was_run: bool,
}
impl<'a> TaskComponent<'a> for Noop {
type Data = ();
fn run(&mut self, _data: &mut Self::Data) -> bool {
self.was_run = true;
true
}
}
fn build_noop_task_runner_system() -> Box<dyn Schedulable> {
SystemBuilder::new("noop_task_runner")
.with_query(task_runner_query::<Noop>())
.build(|_, mut world, _, task_query| run_tasks(&mut world, &mut (), task_query))
}
#[derive(Clone, Debug)]
struct PushValue {
value: usize,
}
impl<'a> TaskComponent<'a> for PushValue {
type Data = Vec<usize>;
fn run(&mut self, data: &mut Self::Data) -> bool {
log::debug!("Task pushing value {}", self.value);
data.push(self.value);
true
}
}
fn build_push_value_task_runner_system() -> Box<dyn Schedulable> {
SystemBuilder::new("example_task_runner")
.write_resource::<Vec<usize>>()
.with_query(task_runner_query::<PushValue>())
.build(|_, mut world, value, task_query| {
run_tasks(&mut world, &mut **value, task_query)
})
}
fn set_up<'a, 'b>() -> (World, Resources, Schedule) {
let mut resources = Resources::default();
resources.insert::<Vec<usize>>(Vec::new());
let world = World::new();
let schedule = Schedule::builder()
.add_system(build_noop_task_runner_system())
.add_system(build_push_value_task_runner_system())
.add_system(build_task_manager_system("task_manager"))
.build();
(world, resources, schedule)
}
fn assemble_task_graph(
make_task_graph: fn() -> TaskGraph,
on_completion: OnCompletion,
world: &mut World,
resources: &mut Resources,
) -> Entity {
resources.insert::<Option<Entity>>(None);
let assemble_system = SystemBuilder::new("assembler")
.write_resource::<Option<Entity>>()
.build(move |mut cmd, _subworld, final_task, _| {
**final_task = Some(make_task_graph().assemble(on_completion, &mut cmd));
});
let mut assemble_schedule = Schedule::builder()
.add_system(assemble_system)
.flush()
.build();
assemble_schedule.execute(world, resources);
resources.get::<Option<Entity>>().unwrap().unwrap()
}
fn assert_task_is_complete(
task: Entity,
is_alive: bool,
world: &mut World,
resources: &mut Resources,
) {
let assert_system =
with_task_components(SystemBuilder::new("asserter")).build(move |_, subworld, _, _| {
if is_alive {
assert!(entity_is_complete(&subworld, task));
}
assert_eq!(subworld.is_alive(task), is_alive);
});
let mut assert_schedule = Schedule::builder().add_system(assert_system).build();
assert_schedule.execute(world, resources);
}
#[test]
fn run_single_task() {
let (mut world, mut resources, mut schedule) = set_up();
fn make_task_graph() -> TaskGraph {
task!(@Noop::default())
}
let root = assemble_task_graph(
make_task_graph,
OnCompletion::None,
&mut world,
&mut resources,
);
schedule.execute(&mut world, &mut resources);
schedule.execute(&mut world, &mut resources);
assert_eq!(
*world.get_component::<Noop>(root).unwrap(),
Noop { was_run: true }
);
assert_task_is_complete(root, true, &mut world, &mut resources);
}
#[test]
fn single_task_deleted_on_completion() {
let (mut world, mut resources, mut schedule) = set_up();
fn make_task_graph() -> TaskGraph {
task!(@Noop::default())
}
let root = assemble_task_graph(
make_task_graph,
OnCompletion::Delete,
&mut world,
&mut resources,
);
schedule.execute(&mut world, &mut resources);
schedule.execute(&mut world, &mut resources);
assert_task_is_complete(root, false, &mut world, &mut resources);
}
#[test]
fn joined_tasks_run_in_order_and_deleted_on_completion() {
let (mut world, mut resources, mut schedule) = set_up();
fn make_task_graph() -> TaskGraph {
seq!(
@PushValue { value: 1 },
@PushValue { value: 2 },
@PushValue { value: 3 }
)
}
let root = assemble_task_graph(
make_task_graph,
OnCompletion::Delete,
&mut world,
&mut resources,
);
schedule.execute(&mut world, &mut resources);
schedule.execute(&mut world, &mut resources);
schedule.execute(&mut world, &mut resources);
schedule.execute(&mut world, &mut resources);
assert_eq!(*resources.get::<Vec<usize>>().unwrap(), vec![1, 2, 3]);
assert_task_is_complete(root, false, &mut world, &mut resources);
}
#[test]
fn all_prongs_of_fork_run_before_join_and_deleted_on_completion() {
let (mut world, mut resources, mut schedule) = set_up();
fn make_task_graph() -> TaskGraph {
seq!(
@PushValue { value: 1 },
fork!(@PushValue { value: 2 }, @PushValue { value: 3 }),
@PushValue { value: 4 }
)
}
let root = assemble_task_graph(
make_task_graph,
OnCompletion::Delete,
&mut world,
&mut resources,
);
schedule.execute(&mut world, &mut resources);
schedule.execute(&mut world, &mut resources);
schedule.execute(&mut world, &mut resources);
schedule.execute(&mut world, &mut resources);
let pushed_values: Vec<usize> = (*resources.get::<Vec<usize>>().unwrap()).clone();
assert!(pushed_values == vec![1, 2, 3, 4] || pushed_values == vec![1, 3, 2, 4]);
assert_task_is_complete(root, false, &mut world, &mut resources);
}
#[test]
fn join_fork_with_nested_fork() {
let (mut world, mut resources, mut schedule) = set_up();
fn make_task_graph() -> TaskGraph {
seq!(
@PushValue { value: 1 },
fork!(
@PushValue { value: 2 },
fork!(@PushValue { value: 3 }, @PushValue { value: 4 })
),
@PushValue { value: 5 }
)
}
let root = assemble_task_graph(
make_task_graph,
OnCompletion::Delete,
&mut world,
&mut resources,
);
schedule.execute(&mut world, &mut resources);
schedule.execute(&mut world, &mut resources);
schedule.execute(&mut world, &mut resources);
schedule.execute(&mut world, &mut resources);
let pushed_values: Vec<usize> = (*resources.get::<Vec<usize>>().unwrap()).clone();
assert!(
pushed_values == vec![1, 2, 3, 4, 5]
|| pushed_values == vec![1, 2, 4, 3, 5]
|| pushed_values == vec![1, 3, 2, 4, 5]
|| pushed_values == vec![1, 3, 4, 2, 5]
|| pushed_values == vec![1, 4, 2, 3, 5]
|| pushed_values == vec![1, 4, 3, 2, 5]
);
assert_task_is_complete(root, false, &mut world, &mut resources);
}
}