Squid::Tasks 1.0.0
C++14 coroutine-based task library for games
Task Manager

Manager that runs and resumes a collection of tasks. More...

Classes

class  TaskManager
 Manager that runs and resumes a collection of tasks. More...
 

Detailed Description

Manager that runs and resumes a collection of tasks.

A TaskManager is a simple manager class that holds an ordered list of tasks and resumes them whenever it is updated.

Running Tasks

There are two primary ways to run tasks on a task manager.

The first method (running an "unmanaged task") is to pass a task into TaskManager::Run(). This will move the task into the task manager and return a TaskHandle that can be used to observe and manage the lifetime of the task (as well as potentially take a return value after the task finishes). With unmanaged tasks, the task manager only holds a weak reference to the task, meaning that the TaskHandle returned by TaskManager::Run() is the only remaining strong reference to the task. Because of this, the caller is entirely responsible for managing the lifetime of the task.

The second method (running a "managed task") is to pass a task into TaskManager::RunManaged(). Like TaskManager::Run(), this will move the task into the task manager and return a WeakTaskHandle that can be used to observe the lifetime of the task (as well as manually kill it, if desired). Unlike unmanaged tasks, the task manager stores a strong reference to the task. Because of this, that caller is not responsible for managing the lifetime of the task. This difference in task ownership means that (unlike an unmanaged task) a managed task can be thought of as a "fire-and-forget" task that will run until either it finishes or until something else explicitly kills it.

Order of Execution

The ordering of task updates within a call to TaskManager::Update() is stable, meaning that the first task that is run on a task manager will remain the first to resume, no matter how many other tasks are run on the task manager (or terminate) in the meantime.

Integration into Actor Classes

Consider the following example of a TaskManager that has been integrated into a TaskActor base class:

class TaskActor : public Actor
{
public:
virtual void OnInitialize() override // Automatically called when this enemy enters the scene
{
Actor::OnInitialize(); // Call the base Actor function
m_taskMgr.RunManaged(ManageActor()); // Run main actor task as a fire-and-forget "managed task"
}
virtual void Tick(float in_dt) override // Automatically called every frame
{
Actor::Tick(in_dt); // Call the base Actor function
m_taskMgr.Update(); // Resume all active tasks once per tick
}
virtual void OnDestroy() override // Automatically called when this enemy leaves the scene
{
m_taskMgr.KillAllTasks(); // Kill all active tasks when we leave the scene
Actor::OnDestroy(); // Call the base Actor function
}
protected:
virtual Task<> ManageActor() // Overridden (in its entirety) by child classes
{
co_await WaitForever(); // Waits forever (doing nothing)
}
TaskManager m_taskMgr;
};
Definition: Task.h:204
Manager that runs and resumes a collection of tasks.
Definition: TaskManager.h:88
Task WaitForever()
Awaiter function that waits forever (only for use in tasks that will be killed externally)
Definition: Task.h:843

In the above example, TaskManager is instantiated once per high-level actor. It is updated once per frame within the Tick() method, and all its tasks are killed when it leaves the scene in OnDestroy(). Lastly, a single entry-point coroutine is run as a managed task when the actor enters the scene. (The above is the conventional method of integration into this style of game engine.)

Note that it is sometimes necessary to have multiple TaskManagers within a single actor. For example, if there are multiple tick functions (such as one for pre-physics updates and one for post-physics updates), then instantiating a second "post-physics" task manager may be desirable.