[−][src]Crate specs_task
Fork-join multitasking for SPECS ECS
Here we expound on the technical details of this module's implementation. For basic usage, see the tests.
In this model, every task is some entity. The entity is allowed to have exactly one component
that implements TaskComponent (it may have other components that don't implement
TaskComponent). The task will be run to completion by the corresponding TaskRunnerSystem.
Every task entity is also a node in a (hopefully acyclic) directed graph. An edge t2 --> t1
means that t2 cannot start until t1 has completed.
In order for tasks to become unblocked, the TaskManagerSystem must run, whence it will
traverse the graph, starting at the "final entities", and check for entities that have
completed, potentially unblocking their parents. In order for a task to be run, it must be the
descendent of a final entity. Entities become final by calling TaskManager::finalize.
Edges can either come from SingleEdge or MultiEdge components, but you should not use these
types directly. You might wonder why we need both. It's a fair question, because adding the
SingleEdge concept does not actually make the model capable of representing any semantically
new graphs. The reason is efficiency.
If you want to implement a fork join like this (note: time is going left to right but the directed edges are going right to left):
r#" ----- t1.1 <--- ----- t2.1 <---
/ \ / \
t0 <------ t1.2 <----<------ t2.2 <---- t3
\ / \ /
----- t1.3 <--- ----- t2.3 <--- "#;You would actually do this by calling TaskManager::make_fork to create two "fork" entities
F1 and F2 that don't have TaskComponents, but they can have both a SingleEdge and a
MultiEdge. Note that the children on the MultiEdge are called "prongs" of the fork.
r#" single single single
t0 <-------- F1 <-------------- F2 <-------- t3
| |
t1.1 <---| t2.1 <--|
t1.2 <---| multi t2.2 <--| multi
t1.3 <---| t2.3 <--| "#;The semantics would be such that this graph is equivalent to the one above. Before any of the
tasks connected to F2 by the MultiEdge could run, the tasks connected by the SingleEdge
({ t0, t1.1, t1.2, t1.3 }) would have to be complete. t3 could only run once all of the
descendents of F2 had completed.
The advantages of this scheme are:
- a traversal of the graph starting from
t3does not visit the same node twice - it is a bit easier to create fork-join graphs with larger numbers of concurrent tasks
- there are fewer edges for the most common use cases
Here's another example with "nested forks" to test your understanding:
r#" With fork entities:
t0 <-------------- FA <----- t2
|
tx <---|
t1 <--- FB <---|
|
ty <-----|
tz <-----|
As time orderings:
t0 < { t1, tx, ty, tz } < t2
t1 < { ty, tz }
Induced graph:
t0 <------- tx <------- t2
^ |
| /------ ty <----|
| v |
----- t1 <---- tz <----- "#;Every user of this module should use it via the TaskManager. It will enforce certain
invariants about the kinds of entities that can be constructed. For example, any entity with a
MultiEdge component is considered a "fork entity", and it is not allowed to have a
TaskComponent or a TaskProgress. Therefore, if you want a task to have multiple children, it
must do so via a fork entity.
These systems must be dispatched for tasks to make progress:
TaskManagerSystemTaskRunnerSystemfor everyT: TaskRunnerused
Potential bugs this module won't detect:
- leaked orphan entities
- graph cycles
- finalizing an entity that has children
- users manually tampering with the
TaskProgress,SingleEdge,MultiEdge, orFinalTagcomponents; these should only be used inside this module
Structs
| AlreadyJoined | This error means that you tried to |
| TaskManager | The main object for users of this module. Manages all non-background task operations. |
| TaskManagerSystem | Traverses all descendents of all finalized entities and unblocks them if possible. |
| TaskRunnerSystem | The counterpart to an implementation |
Enums
| UnexpectedEntity | This error means the entity provided to one of the APIs did not have the expected components. |
Traits
| TaskComponent | An ephemeral component that needs access to |