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

Coroutine-based task handles for controlling execution and lifetime management. More...

Classes

class  Task< tRet, RefType, Resumable >
 

Macros

#define TASK_NAME(...)   co_await NAMESPACE_SQUID::SetDebugName(__VA_ARGS__);
 Macro that instruments a task with a debug name string. Usually at the top of every task coroutine as TASK_NAME(__FUNCTION__)
 

Typedefs

using tTaskCancelFn = std::function< bool()>
 CancelIf/StopIf condition function type.
 
template<typename tRet = void>
using TaskHandle = Task< tRet, eTaskRef::Strong, eTaskResumable::No >
 Non-resumable handle that holds a strong reference to a task.
 
using WeakTask = Task< void, eTaskRef::Weak, eTaskResumable::Yes >
 Resumable handle that holds a weak reference to a task (always void return type)
 
using WeakTaskHandle = Task< void, eTaskRef::Weak, eTaskResumable::No >
 Non-resumable handle that holds a weak reference to a task (always void return type)
 

Enumerations

enum class  eTaskRef { eTaskRef::Strong , eTaskRef::Weak }
 Whether a handle references a task using a strong or weak reference. More...
 
enum class  eTaskResumable { eTaskResumable::Yes , eTaskResumable::No }
 Whether a handle can be resumed (all live tasks have exactly one resumable handle and 0+ non-resumable handles) More...
 
enum class  eTaskStatus { eTaskStatus::Suspended , eTaskStatus::Done }
 Status of a task (whether it is currently suspended or done) More...
 

Detailed Description

Coroutine-based task handles for controlling execution and lifetime management.

Task is a high-level task handle used to manage the lifetime and execution of an underlying coroutine

Handle Types

The Task class is actually a template class that implements 4 user-level handle types:

Handle Type Return Type Resumable? Ref Strength
Task <any type> Yes Strong
WeakTask void Yes Weak
TaskHandle <any type> No Strong
WeakTaskHandle void No Weak

Conversion Rules

It is possible to convert between these 4 types, but not all conversions are permitted. The rules for conversion are:

In simpler terms, this means: a handle can always convert to a handle type with fewer capabilities, but not vice-versa.

Generally-speaking, it would be unsafe to convert in such a way that would add handle properties, hence the motivation for these conversion rules. Care has been taken, however, to provide clear human-readable compile-time error messages if and when an invalid conversion is attempted in code.

Resumability

For a given coroutine instance, it is impossible to have more than a single resumable handle that references it at runtime. We refer to this as the "single-resumer rule". Because both Task and WeakTask are move-only types that cannot be copy-constructed or copy-assigned from other handles, this guarantees at compile-time that there will never be two handles that are able to resume the same underlying coroutine. This compile-time guarantee was implemented after many insidious bugs emerged in gameplay code written using early versions of the Squid::Tasks library.

When a task's single-resumer handle is destroyed, the task is immediately killed. If a coroutine were able to remain suspended without the possibility of ever being resumed again, then any task waiting for it to terminate would deadlock. For this reason, Squid::Tasks enforces that all coroutines must have a valid resumable handle at all times, otherwise they are immediately killed.

(If you are unfamiliar with what it meant by "move-only type", we recommend you research "C++ move semantics" to familiarize yourself.)

Lifetime Management

The default lifetime of a Task's underlying coroutine is determined by the handles the refer to it:

This lifetime management model is essentially the same as a strong-pointer/weak-pointer model, with the added constraint that tasks are killed as soon as they can no longer logically be resumed.

Template Parameters
tRetReturn type of the underlying coroutine (can be void if the coroutine does not co_return a value)
RefTypeWhether this handle holds a strong or weak reference to the underlying coroutine
ResumableWhether this handle can be used to resume the underlying coroutine

Enumeration Type Documentation

◆ eTaskRef

enum class eTaskRef
strong

Whether a handle references a task using a strong or weak reference.

Enumerator
Strong 

Handle will keep the task alive (so long as there exists a valid Resumable handle)

Weak 

Handle will not the task alive.

◆ eTaskResumable

enum class eTaskResumable
strong

Whether a handle can be resumed (all live tasks have exactly one resumable handle and 0+ non-resumable handles)

Enumerator
Yes 

Handle is resumable.

No 

Handle is not resumable.

◆ eTaskStatus

enum class eTaskStatus
strong

Status of a task (whether it is currently suspended or done)

Enumerator
Suspended 

Task is currently suspended.

Done 

Task has terminated and coroutine frame has been destroyed.