blob: 1250022ad90f9b9cf6f79281a62e3db9507502bd [file] [log] [blame] [view]
Francois Doraycc49b74d2018-10-09 13:49:091# Threading and Tasks in Chrome - FAQ
2
3[TOC]
4
Gabriel Charette8917f4c2018-11-22 15:50:285Note: Make sure to read the main [Threading and Tasks](threading_and_tasks.md)
6docs first.
7
Francois Doraycc49b74d2018-10-09 13:49:098## General
9
Gabriel Charette8917f4c2018-11-22 15:50:2810### On which thread will a task run?
Francois Doraycc49b74d2018-10-09 13:49:0911
Gabriel Charette9b6c04072022-04-01 23:22:4612A task is posted through the `base/task/thread_pool.h` API with `TaskTraits`.
Francois Doraycc49b74d2018-10-09 13:49:0913
14* If `TaskTraits` contain `BrowserThread::UI`:
15 * The task runs on the main thread.
16
17* If `TaskTraits` contain `BrowserThread::IO`:
18 * The task runs on the IO thread.
19
20* If `TaskTraits` don't contain `BrowserThread::UI/IO`:
21 * If the task is posted through a `SingleThreadTaskRunner` obtained from
Sami Kyostila831c60b2019-07-31 13:31:2322 `CreateSingleThreadTaskRunner(..., mode)`:
Francois Doraycc49b74d2018-10-09 13:49:0923 * Where `mode` is `SingleThreadTaskRunnerThreadMode::DEDICATED`:
Jared Saulea867ab2021-07-15 17:39:0124 * The task runs on a thread that only runs tasks from that
25 SingleThreadTaskRunner. This is not the main thread nor the IO
26 thread.
Francois Doraycc49b74d2018-10-09 13:49:0927
28 * Where `mode` is `SingleThreadTaskRunnerThreadMode::SHARED`:
Jared Saulea867ab2021-07-15 17:39:0129 * The task runs on a thread that runs tasks from one or many
30 unrelated SingleThreadTaskRunners. This is not the main thread nor
31 the IO thread.
Francois Doraycc49b74d2018-10-09 13:49:0932
33 * Otherwise:
34 * The task runs in a thread pool.
35
36As explained in [Prefer Sequences to Threads](threading_and_tasks.md#Prefer-Sequences-to-Threads),
37tasks should generally run on a sequence in a thread pool rather than on a
38dedicated thread.
39
Francois Doray571f85a92019-02-01 17:02:1640### Does release of a TaskRunner block on posted tasks?
41
42Releasing a TaskRunner reference does not wait for tasks previously posted to
43the TaskRunner to complete their execution. Tasks can run normally after the
44last client reference to the TaskRunner to which they were posted has been
45released and it can even be kept alive indefinitely through
Sean Maher70f2942932023-01-04 22:15:0646`SequencedTaskRunner::GetCurrentDefault()` or
47`SingleThreadTaskRunner::GetCurrentDefault()`.
Francois Doray571f85a92019-02-01 17:02:1648
49If you want some state to be deleted only after all tasks currently posted to a
50SequencedTaskRunner have run, store that state in a helper object and schedule
51deletion of that helper object on the SequencedTaskRunner using
52`base::OnTaskRunnerDeleter` after posting the last task. See
53[example CL](https://crrev.com/c/1416271/15/chrome/browser/performance_monitor/system_monitor.h).
54But be aware that any task posting back to its "current" sequence can enqueue
55itself after that "last" task.
56
Gabriel Charette8917f4c2018-11-22 15:50:2857## Making blocking calls (which do not use the CPU)
Francois Doraycc49b74d2018-10-09 13:49:0958
Etienne Pierre-dorayd710e1522018-10-26 16:22:2359### How to make a blocking call without preventing other tasks from being scheduled?
Francois Doraycc49b74d2018-10-09 13:49:0960
Etienne Pierre-dorayd710e1522018-10-26 16:22:2361The steps depend on where the task runs (see [Where will a task run?](#On-what-thread-will-a-task-run_)).
Francois Doraycc49b74d2018-10-09 13:49:0962
63If the task runs in a thread pool:
64
Gabriel Charette8917f4c2018-11-22 15:50:2865* Annotate the scope that may block with
Francois Doraycc49b74d2018-10-09 13:49:0966 `ScopedBlockingCall(BlockingType::MAY_BLOCK/WILL_BLOCK)`. A few milliseconds
67 after the annotated scope is entered, the capacity of the thread pool is
68 incremented. This ensures that your task doesn't reduce the number of tasks
69 that can run concurrently on the CPU. If the scope exits, the thread pool
Etienne Pierre-dorayd710e1522018-10-26 16:22:2370 capacity goes back to normal.
Francois Doraycc49b74d2018-10-09 13:49:0971
72If the task runs on the main thread, the IO thread or a `SHARED
73SingleThreadTaskRunner`:
74
75* Blocking on one of these threads will cause breakages. Move your task to a
76 thread pool (or to a `DEDICATED SingleThreadTaskRunner` if necessary - see
77 [Prefer Sequences to Threads](threading_and_tasks.md#Prefer-Sequences-to-Threads)).
78
79If the task runs on a `DEDICATED SingleThreadTaskRunner`:
80
Gabriel Charette8917f4c2018-11-22 15:50:2881* Annotate the scope that may block with
Francois Doraycc49b74d2018-10-09 13:49:0982 `ScopedBlockingCall(BlockingType::MAY_BLOCK/WILL_BLOCK)`. The annotation is a
Gabriel Charette8917f4c2018-11-22 15:50:2883 no-op that documents the blocking behavior (and makes it pass assertions).
84 Tasks posted to the same `DEDICATED SingleThreadTaskRunner` won't run until
85 your blocking task returns (they will never run if the blocking task never
86 returns).
Francois Doraycc49b74d2018-10-09 13:49:0987
Etienne Pierre-doray338d61c2018-10-16 12:35:3288[base/threading/scoped_blocking_call.h](https://cs.chromium.org/chromium/src/base/threading/scoped_blocking_call.h)
Jared Saulea867ab2021-07-15 17:39:0189explains the difference between `MAY_BLOCK` and `WILL_BLOCK` and gives
Gabriel Charette8917f4c2018-11-22 15:50:2890examples of blocking operations.
Francois Doraycc49b74d2018-10-09 13:49:0991
Etienne Pierre-dorayd710e1522018-10-26 16:22:2392### How to make a blocking call that may never return without preventing other tasks from being scheduled?
93
94If you can't avoid making a call to a third-party library that may block off-
95CPU, follow recommendations in [How to make a blocking call without affecting
96other tasks?](#How-to-make-a-blocking-call-without-affecting-other-tasks_).
97This ensures that a current task doesn't prevent other tasks from running even
98if it never returns.
99
100Since tasks posted to the same sequence can't run concurrently, it is advisable
101to run tasks that may block indefinitely in
102[parallel](threading_and_tasks.md#posting-a-parallel-task) rather than in
Gabriel Charette8917f4c2018-11-22 15:50:28103[sequence](threading_and_tasks.md#posting-a-sequenced-task) (unless posting many
104such tasks at which point sequencing can be a useful tool to prevent flooding).
Etienne Pierre-dorayd710e1522018-10-26 16:22:23105
Etienne Pierre-doray338d61c2018-10-16 12:35:32106### Do calls to blocking //base APIs need to be annotated with ScopedBlockingCall?
107
Jared Saulea867ab2021-07-15 17:39:01108No. All blocking //base APIs (e.g. `base::ReadFileToString`, `base::File::Read`,
109`base::SysInfo::AmountOfFreeDiskSpace`, `base::WaitableEvent::Wait`, etc.) have
110their own internal annotations. See
Etienne Pierre-doray338d61c2018-10-16 12:35:32111[base/threading/scoped_blocking_call.h](https://cs.chromium.org/chromium/src/base/threading/scoped_blocking_call.h).
112
113### Can multiple ScopedBlockingCall be nested for the purpose of documentation?
114
115Nested `ScopedBlockingCall` are supported. Most of the time, the inner
Jared Saulea867ab2021-07-15 17:39:01116ScopedBlockingCalls will no-op (the exception is `WILL_BLOCK` nested in `MAY_BLOCK`).
Etienne Pierre-doray338d61c2018-10-16 12:35:32117As such, it is permitted to add a ScopedBlockingCall in the scope where a function
118that is already annotated is called for documentation purposes.:
119
120```cpp
121Data GetDataFromNetwork() {
122 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
123 // Fetch data from network.
124 ...
125 return data;
126}
127
128void ProcessDataFromNetwork() {
129 Data data;
130 {
131 // Document the blocking behavior with a ScopedBlockingCall.
132 // Permitted, but not required since GetDataFromNetwork() is itself annotated.
133 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
134 data = GetDataFromNetwork();
135 }
136 CPUIntensiveProcessing(data);
137}
138```
139
140 However, CPU usage should always be minimal within the scope of
141`ScopedBlockingCall`. See
142[base/threading/scoped_blocking_call.h](https://cs.chromium.org/chromium/src/base/threading/scoped_blocking_call.h).
143
144
Francois Doraycc49b74d2018-10-09 13:49:09145## Sequences
146
147### How to migrate from SingleThreadTaskRunner to SequencedTaskRunner?
148
149The following mappings can be useful when migrating code from a
150`SingleThreadTaskRunner` to a `SequencedTaskRunner`:
151
152* base::SingleThreadTaskRunner -> base::SequencedTaskRunner
153 * SingleThreadTaskRunner::BelongsToCurrentThread() -> SequencedTaskRunner::RunsTasksInCurrentSequence()
Sean Maher70f2942932023-01-04 22:15:06154* base::SingleThreadTaskRunner::CurrentDefaultHandle ->
155 base::SequencedTaskRunnerHandle::CurrentDefaultHandle
Francois Doraycc49b74d2018-10-09 13:49:09156* THREAD_CHECKER -> SEQUENCE_CHECKER
157* base::ThreadLocalStorage::Slot -> base::SequenceLocalStorageSlot
158* BrowserThread::DeleteOnThread -> base::OnTaskRunnerDeleter / base::RefCountedDeleteOnSequence
159* BrowserMessageFilter::OverrideThreadForMessage() -> BrowserMessageFilter::OverrideTaskRunnerForMessage()
Sami Kyostila831c60b2019-07-31 13:31:23160* CreateSingleThreadTaskRunner() -> CreateSequencedTaskRunner()
161 * Every CreateSingleThreadTaskRunner() usage, outside of
Gabriel Charette8917f4c2018-11-22 15:50:28162 BrowserThread::UI/IO, should be accompanied with a comment and ideally a
163 bug to make it sequence when the sequence-unfriendly dependency is
164 addressed.
Francois Doraycc49b74d2018-10-09 13:49:09165
166### How to ensure mutual exclusion between tasks posted by a component?
167
Sami Kyostila831c60b2019-07-31 13:31:23168Create a `SequencedTaskRunner` using `CreateSequencedTaskRunner()` and
Francois Doraycc49b74d2018-10-09 13:49:09169store it on an object that can be accessed from all the PostTask() call sites
Gabriel Charette8917f4c2018-11-22 15:50:28170that require mutual exclusion. If there isn't a shared object that can own a
Francois Doraycc49b74d2018-10-09 13:49:09171common `SequencedTaskRunner`, use
172`Lazy(Sequenced|SingleThread|COMSTA)TaskRunner` in an anonymous namespace.
173
174## Tests
175
176### How to test code that posts tasks?
177
178If the test uses `BrowserThread::UI/IO`, instantiate a
Gabriel Charette798fde72019-08-20 22:24:04179`content::BrowserTaskEnvironment` for the scope of the test. Call
180`BrowserTaskEnvironment::RunUntilIdle()` to wait until all tasks have run.
Francois Doraycc49b74d2018-10-09 13:49:09181
182If the test doesn't use `BrowserThread::UI/IO`, instantiate a
Gabriel Charette694c3c332019-08-19 14:53:05183`base::test::TaskEnvironment` for the scope of the test. Call
184`base::test::TaskEnvironment::RunUntilIdle()` to wait until all tasks have
Francois Doraycc49b74d2018-10-09 13:49:09185run.
186
187In both cases, you can run tasks until a condition is met. A test that waits for
188a condition to be met is easier to understand and debug than a test that waits
189for all tasks to run.
190
191```cpp
192int g_condition = false;
193
194base::RunLoop run_loop;
Gabriel Charette1f57c9b82022-03-16 22:54:34195base::ThreadPool::PostTask(FROM_HERE, {}, base::BindOnce(
Thiabaud Engelbrechtc0a197e62025-05-14 18:53:45196 [] (base::OnceClosure quit_closure) {
Francois Doraycc49b74d2018-10-09 13:49:09197 g_condition = true;
198 std::move(quit_closure).Run();
199 }, run_loop.QuitClosure()));
200
201// Runs tasks until the quit closure is invoked.
202run_loop.Run();
203
204EXPECT_TRUE(g_condition);
205```
206
207## Your question hasn't been answered?
208
Gabriel Charette8917f4c2018-11-22 15:50:28209 1. Check the main [Threading and Tasks](threading_and_tasks.md) docs.
210 2. Ping
Francois Doraycc49b74d2018-10-09 13:49:09211[scheduler-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/scheduler-dev).