Squid::Tasks 1.0.0
C++14 coroutine-based task library for games
|
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... | |
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
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 |
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.
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.)
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.
tRet | Return type of the underlying coroutine (can be void if the coroutine does not co_return a value) |
RefType | Whether this handle holds a strong or weak reference to the underlying coroutine |
Resumable | Whether this handle can be used to resume the underlying coroutine |
|
strong |
|
strong |
|
strong |