Threading & scheduling
This directory contains three examples of threading & primitives in Chromium's
//base
library that build off of the basic cpp101
threading and task runner
introduction. In particular, these examples aim to give more insight into
how task scheduling works by introducing TaskQueue
as the basic unit of
scheduling, and showing how multiple TaskQueue
s can interact within the same
thread and across threads.
Additionally, these examples show direct usage of some of the foundational
scheduling primitives in //base
as they would be used in any program, to
demystify their usage and help illustrate how core pieces of Chromium work under
the hood.
These examples offer a deep dive into the threading & scheduling machinery that
Chromium developers will rarely interact with directly. In practice, much of
Chromium's threading & scheduling is exposed through things like
base::ThreadPool
, base::SequencedTaskRunner::GetCurrentDefault()
, or
ExecutionContext::GetTaskRunner()
in Blink, etc.
01-single-task-queue
This first example is the simplest, but illustrates direct usage of the following task scheduling primitives:
SequenceManager(Impl)
MessagePump
TaskQueue
/TaskRunner
RunLoop
This example goes further than the ones in cpp101/
, as those defer the setup
of the main thread scheduling infra to base::test::TaskEnvironment
, whereas
this example shows how to manually use such infrastructure and customize it
accordingly.
02-task-queue-priorities
This example shows how TaskQueue
s allow a thread to multiplex many sources of
tasks. This effectively means that each TaskQueue
is a "sequence", allowing
the same physical thread to host multiple sequences that can be scheduled
independently. This is an inversion from the standard usage of "sequence", which
often involves a sequential ordering of tasks that may opaquely span multiple
physical threads.
This example works by creating two TaskQueue
s with different priorities to
show that the ordering across queues is not guaranteed, while the ordering of
non-delayed tasks within a queue — regardless of which TaskRunner
is
used to post the task — is always FIFO order.
03-randomized-task-queues
(Requires DCHECKs to be enabled to build. Either set is_debug = true
or
dcheck_always_enabled = true
in your gn args).
This example is similar to the previous one, however we schedule two
identical-priority TaskQueue
s independently from each other using a
randomized task selection seed option exposed via the SequenceManager
API. This shows that in our contrived example, ordering across queues of the
same priority is not guaranteed to be in FIFO task posting order, while the
ordering of non-delayed tasks within the same queue is still always FIFO
order.
Note that in practice and by default, the ordering of tasks across task queues with the same priority is in fact guaranteed to be in FIFO posting-order because legacy code has come to depend on this behavior, although it should not be relied upon, as queues can dynamically change priority and get frozen/unfrozen arbitrarily.
04-multiple-threads
So far the threading and task posting examples have been either single-threaded,
or utilize the thread pool. But in Chromium, especially in renderer processes,
it's common to post tasks on specific threads that the platform maintains.
That's exactly what the scenario that this example reproduces. First we setup
the main thread task scheduling infra, and then spin up a new base::Thread
which represents a new physical thread. Then we explicitly post tasks back and
forth between the main and secondary thread, via a cross-thread TaskRunner
.