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.
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.
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.
(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.
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
.