blob: 8812d93005f205bdf234b52f0613cedd89951da5 [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
46`SequencedTaskRunnerHandle::Get()` or `ThreadTaskRunnerHandle::Get()`.
47
48If you want some state to be deleted only after all tasks currently posted to a
49SequencedTaskRunner have run, store that state in a helper object and schedule
50deletion of that helper object on the SequencedTaskRunner using
51`base::OnTaskRunnerDeleter` after posting the last task. See
52[example CL](https://crrev.com/c/1416271/15/chrome/browser/performance_monitor/system_monitor.h).
53But be aware that any task posting back to its "current" sequence can enqueue
54itself after that "last" task.
55
Gabriel Charette8917f4c2018-11-22 15:50:2856## Making blocking calls (which do not use the CPU)
Francois Doraycc49b74d2018-10-09 13:49:0957
Etienne Pierre-dorayd710e152018-10-26 16:22:2358### How to make a blocking call without preventing other tasks from being scheduled?
Francois Doraycc49b74d2018-10-09 13:49:0959
Etienne Pierre-dorayd710e152018-10-26 16:22:2360The 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:0961
62If the task runs in a thread pool:
63
Gabriel Charette8917f4c2018-11-22 15:50:2864* Annotate the scope that may block with
Francois Doraycc49b74d2018-10-09 13:49:0965 `ScopedBlockingCall(BlockingType::MAY_BLOCK/WILL_BLOCK)`. A few milliseconds
66 after the annotated scope is entered, the capacity of the thread pool is
67 incremented. This ensures that your task doesn't reduce the number of tasks
68 that can run concurrently on the CPU. If the scope exits, the thread pool
Etienne Pierre-dorayd710e152018-10-26 16:22:2369 capacity goes back to normal.
Francois Doraycc49b74d2018-10-09 13:49:0970
71If the task runs on the main thread, the IO thread or a `SHARED
72SingleThreadTaskRunner`:
73
74* Blocking on one of these threads will cause breakages. Move your task to a
75 thread pool (or to a `DEDICATED SingleThreadTaskRunner` if necessary - see
76 [Prefer Sequences to Threads](threading_and_tasks.md#Prefer-Sequences-to-Threads)).
77
78If the task runs on a `DEDICATED SingleThreadTaskRunner`:
79
Gabriel Charette8917f4c2018-11-22 15:50:2880* Annotate the scope that may block with
Francois Doraycc49b74d2018-10-09 13:49:0981 `ScopedBlockingCall(BlockingType::MAY_BLOCK/WILL_BLOCK)`. The annotation is a
Gabriel Charette8917f4c2018-11-22 15:50:2882 no-op that documents the blocking behavior (and makes it pass assertions).
83 Tasks posted to the same `DEDICATED SingleThreadTaskRunner` won't run until
84 your blocking task returns (they will never run if the blocking task never
85 returns).
Francois Doraycc49b74d2018-10-09 13:49:0986
Etienne Pierre-doray338d61c2018-10-16 12:35:3287[base/threading/scoped_blocking_call.h](https://cs.chromium.org/chromium/src/base/threading/scoped_blocking_call.h)
Jared Saulea867ab2021-07-15 17:39:0188explains the difference between `MAY_BLOCK` and `WILL_BLOCK` and gives
Gabriel Charette8917f4c2018-11-22 15:50:2889examples of blocking operations.
Francois Doraycc49b74d2018-10-09 13:49:0990
Etienne Pierre-dorayd710e152018-10-26 16:22:2391### How to make a blocking call that may never return without preventing other tasks from being scheduled?
92
93If you can't avoid making a call to a third-party library that may block off-
94CPU, follow recommendations in [How to make a blocking call without affecting
95other tasks?](#How-to-make-a-blocking-call-without-affecting-other-tasks_).
96This ensures that a current task doesn't prevent other tasks from running even
97if it never returns.
98
99Since tasks posted to the same sequence can't run concurrently, it is advisable
100to run tasks that may block indefinitely in
101[parallel](threading_and_tasks.md#posting-a-parallel-task) rather than in
Gabriel Charette8917f4c2018-11-22 15:50:28102[sequence](threading_and_tasks.md#posting-a-sequenced-task) (unless posting many
103such tasks at which point sequencing can be a useful tool to prevent flooding).
Etienne Pierre-dorayd710e152018-10-26 16:22:23104
Etienne Pierre-doray338d61c2018-10-16 12:35:32105### Do calls to blocking //base APIs need to be annotated with ScopedBlockingCall?
106
Jared Saulea867ab2021-07-15 17:39:01107No. All blocking //base APIs (e.g. `base::ReadFileToString`, `base::File::Read`,
108`base::SysInfo::AmountOfFreeDiskSpace`, `base::WaitableEvent::Wait`, etc.) have
109their own internal annotations. See
Etienne Pierre-doray338d61c2018-10-16 12:35:32110[base/threading/scoped_blocking_call.h](https://cs.chromium.org/chromium/src/base/threading/scoped_blocking_call.h).
111
112### Can multiple ScopedBlockingCall be nested for the purpose of documentation?
113
114Nested `ScopedBlockingCall` are supported. Most of the time, the inner
Jared Saulea867ab2021-07-15 17:39:01115ScopedBlockingCalls will no-op (the exception is `WILL_BLOCK` nested in `MAY_BLOCK`).
Etienne Pierre-doray338d61c2018-10-16 12:35:32116As such, it is permitted to add a ScopedBlockingCall in the scope where a function
117that is already annotated is called for documentation purposes.:
118
119```cpp
120Data GetDataFromNetwork() {
121 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
122 // Fetch data from network.
123 ...
124 return data;
125}
126
127void ProcessDataFromNetwork() {
128 Data data;
129 {
130 // Document the blocking behavior with a ScopedBlockingCall.
131 // Permitted, but not required since GetDataFromNetwork() is itself annotated.
132 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
133 data = GetDataFromNetwork();
134 }
135 CPUIntensiveProcessing(data);
136}
137```
138
139 However, CPU usage should always be minimal within the scope of
140`ScopedBlockingCall`. See
141[base/threading/scoped_blocking_call.h](https://cs.chromium.org/chromium/src/base/threading/scoped_blocking_call.h).
142
143
Francois Doraycc49b74d2018-10-09 13:49:09144## Sequences
145
146### How to migrate from SingleThreadTaskRunner to SequencedTaskRunner?
147
148The following mappings can be useful when migrating code from a
149`SingleThreadTaskRunner` to a `SequencedTaskRunner`:
150
151* base::SingleThreadTaskRunner -> base::SequencedTaskRunner
152 * SingleThreadTaskRunner::BelongsToCurrentThread() -> SequencedTaskRunner::RunsTasksInCurrentSequence()
153* base::ThreadTaskRunnerHandle -> base::SequencedTaskRunnerHandle
154* THREAD_CHECKER -> SEQUENCE_CHECKER
155* base::ThreadLocalStorage::Slot -> base::SequenceLocalStorageSlot
156* BrowserThread::DeleteOnThread -> base::OnTaskRunnerDeleter / base::RefCountedDeleteOnSequence
157* BrowserMessageFilter::OverrideThreadForMessage() -> BrowserMessageFilter::OverrideTaskRunnerForMessage()
Sami Kyostila831c60b2019-07-31 13:31:23158* CreateSingleThreadTaskRunner() -> CreateSequencedTaskRunner()
159 * Every CreateSingleThreadTaskRunner() usage, outside of
Gabriel Charette8917f4c2018-11-22 15:50:28160 BrowserThread::UI/IO, should be accompanied with a comment and ideally a
161 bug to make it sequence when the sequence-unfriendly dependency is
162 addressed.
Francois Doraycc49b74d2018-10-09 13:49:09163
164### How to ensure mutual exclusion between tasks posted by a component?
165
Sami Kyostila831c60b2019-07-31 13:31:23166Create a `SequencedTaskRunner` using `CreateSequencedTaskRunner()` and
Francois Doraycc49b74d2018-10-09 13:49:09167store it on an object that can be accessed from all the PostTask() call sites
Gabriel Charette8917f4c2018-11-22 15:50:28168that require mutual exclusion. If there isn't a shared object that can own a
Francois Doraycc49b74d2018-10-09 13:49:09169common `SequencedTaskRunner`, use
170`Lazy(Sequenced|SingleThread|COMSTA)TaskRunner` in an anonymous namespace.
171
172## Tests
173
174### How to test code that posts tasks?
175
176If the test uses `BrowserThread::UI/IO`, instantiate a
Gabriel Charette798fde72019-08-20 22:24:04177`content::BrowserTaskEnvironment` for the scope of the test. Call
178`BrowserTaskEnvironment::RunUntilIdle()` to wait until all tasks have run.
Francois Doraycc49b74d2018-10-09 13:49:09179
180If the test doesn't use `BrowserThread::UI/IO`, instantiate a
Gabriel Charette694c3c332019-08-19 14:53:05181`base::test::TaskEnvironment` for the scope of the test. Call
182`base::test::TaskEnvironment::RunUntilIdle()` to wait until all tasks have
Francois Doraycc49b74d2018-10-09 13:49:09183run.
184
185In both cases, you can run tasks until a condition is met. A test that waits for
186a condition to be met is easier to understand and debug than a test that waits
187for all tasks to run.
188
189```cpp
190int g_condition = false;
191
192base::RunLoop run_loop;
Gabriel Charette1f57c9b82022-03-16 22:54:34193base::ThreadPool::PostTask(FROM_HERE, {}, base::BindOnce(
Francois Doraycc49b74d2018-10-09 13:49:09194 [] (base::OnceClosure closure) {
195 g_condition = true;
196 std::move(quit_closure).Run();
197 }, run_loop.QuitClosure()));
198
199// Runs tasks until the quit closure is invoked.
200run_loop.Run();
201
202EXPECT_TRUE(g_condition);
203```
204
205## Your question hasn't been answered?
206
Gabriel Charette8917f4c2018-11-22 15:50:28207 1. Check the main [Threading and Tasks](threading_and_tasks.md) docs.
208 2. Ping
Francois Doraycc49b74d2018-10-09 13:49:09209[scheduler-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/scheduler-dev).