The scheduler
A scheduler is a set of OS threads that run tasks as fibers. You create it with CreateTaskScheduler, start it, add tasks, and stop and destroy it when all tasks have finished.
Kinds
| SchedulerKind | What it is |
|---|---|
Default |
A work-stealing scheduler over several threads. This is the one you want. |
TaskSwap |
A single-threaded scheduler that swaps two tasks under an ITaskSwitchPolicy. For special cases; it ignores priorities and cannot start tasks by time. |
Configuration
SchedulerInfo is copied by CreateTaskScheduler; zero-initialized it gives
sensible defaults:
| Field | Meaning |
|---|---|
| numberOfThreads | Scheduler threads; 0 means one per logical processor. |
| taskQueueCapacity | Capacity of each of the four task queues (see below); 0 means 4096. |
| threadCallback, threadCallbackContext | Called on every scheduler thread when it starts and when it exits — to set affinity, thread priority or thread-local state; see Thread callbacks. |
| taskSwitchPolicy | Only for TaskSwap. |
| version | Leave it at its default. |
CreateTaskScheduler returns nullptr if the configuration is invalid — for example a queue
capacity above MaximumTaskQueueCapacity.
Queues and their capacity
The default scheduler keeps four bounded queues: new tasks and resumed tasks, each for the two priorities (Priorities). A thread looks for work in its own queues first and steals from the others when it runs dry.
The queues never grow.
Important
taskQueueCapacity must be larger than the number of tasks that can be runnable at the same time per queue — new tasks per priority, and resumed or yielded tasks per priority. Suspended tasks do not count. Filling a queue is invalid use. The value is rounded up to a power of two; each slot takes one cache line.
Lifecycle
- Create — CreateTaskScheduler.
- Add tasks — AddTask may be called before Start; those tasks run once the scheduler has started. It never blocks and may be called from any thread or task.
- Start — once.
- Wait for your tasks — with a TaskGroup, WaitableTask or your own signal.
- Stop — Stop ends the threads and returns when they have exited. It must be called from outside the scheduler's threads, and it does not run queued or suspended tasks to completion.
- Destroy — Destroy, never
delete.
The calling task's scheduler
Inside a task, GetTaskScheduler returns the scheduler that runs it, and Task::Schedule adds a task to that scheduler. Most functions that act on "the calling task" — YieldTask, SetTaskPriority, SleepTask — are free functions that find the scheduler themselves.
Several schedulers
A process may run several schedulers, each with its own threads. A common split is one scheduler for computation and one for tasks that mostly wait for I/O, so that compute-bound tasks never delay the tasks that keep the I/O going. A suspended task always resumes on the scheduler that suspended it, so primitives can be shared freely between schedulers.