Tasks
A task is an object whose Run method the scheduler calls on one of its threads. The object
belongs to you: the scheduler never allocates, copies or deletes it.
The interface
At the bottom is ITask from Core.h:
| Member | Purpose |
|---|---|
| Run | The task's body. noexcept; it may wait, yield and sleep. |
| OnTaskExited | Called by the core once the task has exited — the earliest moment the object may be destroyed or added again. |
| SetNotifyCallback | Lets code that did not create the task observe its end (a task group uses it). |
| GetTimerData | Storage for starting the task by time; nullptr means it cannot be started later. |
The library's task classes
You rarely implement ITask yourself:
- Task — the base class to derive from. It provides the timer storage, forwards the exit notification and adds Schedule (add to the calling task's scheduler, optionally after a delay).
- WaitableTask / WaitableTask_s — a
Taskthat others can Wait for. - LambdaTask — a
Taskthat runs a callable; Task::Create builds one. It is returned by value and not scheduled yet: keep it in a variable that outlives the task.
Running a task
AddTask (or Schedule) queues the task; a scheduler thread calls
Run on a fiber. Run runs once per AddTask. While it runs, the task may:
- wait on a primitive or for I/O — it is suspended and the thread runs other tasks;
- yield — stay runnable but let others go first;
- sleep — with timers enabled.
When Run returns, the task has exited: the core calls OnTaskExited, and from then on it no
longer touches the object.
Important
A task object must stay alive and in place from AddTask until its OnTaskExited has been
called, and may be added again only after that. A task that waits on a primitive must not outlive
it either — see Lifetimes and sizing.
Exceptions
Run is noexcept. An exception that escapes it ends the process. Catch what you expect inside
the task and report it through the task's own state.
A task may change threads
Each time a task is resumed, any thread of its scheduler may pick it up. Code in a task therefore
must not keep thread-affine state across a wait: no OS locks held over a Wait
(std::mutex, CRITICAL_SECTION, SRWLOCK), no assumptions about thread_local values or the
thread id. Use the library's CriticalSection, which is owned by the task,
not by the thread.