[][src]Crate specs_task

A multitasking module that supports the fork-join model. Implemented on top of 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:

 r#"     ---> t1.1 ---
       /               \
     t2 ----> t1.2 -----> t0
       \               /
         ---> t1.3 ---       "#;

You would actually do this by calling TaskManager::make_fork to create a "fork" entity called F that doesn't have a TaskComponent, but it has a SingleEdge from t2 to t0, and a MultiEdge from t2 to { t1.1, t1.2, t1.3 }. Note that the children on the MultiEdge are called "prongs" of the fork.

 r#"  t2 --> F --> t0
             |
             | --> t1.1
             | --> t1.2
             | --> t1.3  "#;

The semantics would be such that this graph is equivalent to the one above. Before any of the tasks connected to F by the MultiEdge could run, the task connected by the SingleEdge (t0) would have to be complete. t2 could only run once all of the children of F had completed.

The advantages of this scheme are:

  • a traversal of the graph starting from t2 does 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

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:

  • TaskManagerSystem
  • TaskRunnerSystem for every T: TaskRunner used

This module can be dangerous when used improperly due to the dynamic nature of SPECS. Potential bugs not handled by this module:

  • leaked orphan entities
  • graph cycles
  • DO NOT manually touch the storages for task module components! Always go through the TaskManager.

Structs

FinalTag

WARNING: only public because TaskManager is public. DO NOT USE.

MultiEdge

WARNING: only public because TaskManager is public. DO NOT USE.

SingleEdge

WARNING: only public because TaskManager is public. DO NOT USE.

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.

TaskProgress

WARNING: only public because TaskManager is public. DO NOT USE.

TaskRunnerSystem

The counterpart to an implementation TaskComponent. Runs tasks until completion.

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 SystemData to run some task. Will be run until is_complete by the TaskRunnerSystem<T>.