Preliminary. ThinkMeta ConcurrentTasks and this documentation are preliminary; everything is subject to change.

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

  1. Create — CreateTaskScheduler.
  2. 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.
  3. Start — once.
  4. Wait for your tasks — with a TaskGroup, WaitableTask or your own signal.
  5. 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.
  6. 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.

See also