blob: 5418910f9f6082c95ce192a1c99eaa5890992a42 [file] [log] [blame] [view]
fdoraybacba4a22017-05-10 21:10:001# Threading and Tasks in Chrome
2
3[TOC]
4
Gabriel Charette8917f4c2018-11-22 15:50:285Note: See [Threading and Tasks FAQ](threading_and_tasks_faq.md) for more
6examples.
7
fdoraybacba4a22017-05-10 21:10:008## Overview
9
Gabriel Charette90480312018-02-16 15:10:0510Chromium is a very multithreaded product. We try to keep the UI as responsive as
11possible, and this means not blocking the UI thread with any blocking I/O or
12other expensive operations. Our approach is to use message passing as the way of
13communicating between threads. We discourage locking and threadsafe
14objects. Instead, objects live on only one thread, we pass messages between
15threads for communication, and we use callback interfaces (implemented by
16message passing) for most cross-thread requests.
17
Gabriel Charette364a16a2019-02-06 21:12:1518### Nomenclature
19 * **Thread-unsafe**: The vast majority of types in Chrome are thread-unsafe by
20 design. Such types/methods cannot be accessed concurrently from different
21 threads. They should either be accessed from a single
22 `base::SequencedTaskRunner` (this should be enforced by a `SEQUENCE_CHECKER`)
23 or be accessed with external synchronization (e.g. locks -- but
24 [prefer sequences](#Using-Sequences-Instead-of-Locks)).
25 * **Thread-affine**: Such types/methods need to be always accessed from the
26 same physical thread (i.e. from the same `base::SingleThreadTaskRunner`).
27 Short of using a third-party API or having a leaf dependency which is
28 thread-affine: there's pretty much no reason for a type to be thread-affine
29 in Chrome. Note that `base::SingleThreadTaskRunner` is-a
30 `base::SequencedTaskRunner` so thread-affine is a subset of thread-unsafe.
31 * **Thread-safe**: Such types/methods can be safely accessed concurrently.
32 * **Thread-compatible**: Such types/methods are initialized once in a
33 thread-safe manner (either in the single-threaded phase of startup or lazily
34 through a thread-safe static-local-initialization paradigm a la
35 `base::NoDestructor`) and can forever after be accessed concurrently in a
36 read-only manner.
37 * **Sequence-friendly**: Such types/methods are thread-unsafe types which
38 support being invoked from a `base::SequencedTaskRunner`. Ideally this would
39 be the case for all thread-unsafe types but legacy code sometimes has
40 overzealous checks that enforce thread-affinity in mere thread-unsafe
41 scenarios. See [Prefer Sequences to Threads](#prefer-sequences-to-threads)
42 below for more details.
43
fdoraybacba4a22017-05-10 21:10:0044### Threads
45
46Every Chrome process has
47
48* a main thread
49 * in the browser process: updates the UI
50 * in renderer processes: runs most of Blink
51* an IO thread
52 * in the browser process: handles IPCs and network requests
53 * in renderer processes: handles IPCs
54* a few more special-purpose threads
55* and a pool of general-purpose threads
56
57Most threads have a loop that gets tasks from a queue and runs them (the queue
58may be shared between multiple threads).
59
60### Tasks
61
62A task is a `base::OnceClosure` added to a queue for asynchronous execution.
63
64A `base::OnceClosure` stores a function pointer and arguments. It has a `Run()`
65method that invokes the function pointer using the bound arguments. It is
66created using `base::BindOnce`. (ref. [Callback<> and Bind()
67documentation](callback.md)).
68
69```
70void TaskA() {}
71void TaskB(int v) {}
72
73auto task_a = base::BindOnce(&TaskA);
74auto task_b = base::BindOnce(&TaskB, 42);
75```
76
77A group of tasks can be executed in one of the following ways:
78
79* [Parallel](#Posting-a-Parallel-Task): No task execution ordering, possibly all
80 at once on any thread
81* [Sequenced](#Posting-a-Sequenced-Task): Tasks executed in posting order, one
82 at a time on any thread.
83* [Single Threaded](#Posting-Multiple-Tasks-to-the-Same-Thread): Tasks executed
84 in posting order, one at a time on a single thread.
85 * [COM Single Threaded](#Posting-Tasks-to-a-COM-Single-Thread-Apartment-STA_Thread-Windows_):
86 A variant of single threaded with COM initialized.
87
gab2a4576052017-06-07 23:36:1288### Prefer Sequences to Threads
89
Gabriel Charetteb86e5fe62017-06-08 19:39:2890**Sequenced execution mode is far preferred to Single Threaded** in scenarios
gab2a4576052017-06-07 23:36:1291that require mere thread-safety as it opens up scheduling paradigms that
92wouldn't be possible otherwise (sequences can hop threads instead of being stuck
93behind unrelated work on a dedicated thread). Ability to hop threads also means
94the thread count can dynamically adapt to the machine's true resource
Gabriel Charette90480312018-02-16 15:10:0595availability (increased parallelism on bigger machines, avoids trashing
96resources on smaller machines).
gab2a4576052017-06-07 23:36:1297
98Many core APIs were recently made sequence-friendly (classes are rarely
Gabriel Charette364a16a2019-02-06 21:12:1599thread-affine -- i.e. only when using third-party APIs that are thread-affine;
100even ThreadLocalStorage has a SequenceLocalStorage equivalent). But the codebase
101has long evolved assuming single-threaded contexts... If your class could run on
102a sequence but is blocked by an overzealous use of
gab2a4576052017-06-07 23:36:12103ThreadChecker/ThreadTaskRunnerHandle/SingleThreadTaskRunner in a leaf
104dependency, consider fixing that dependency for everyone's benefit (or at the
105very least file a blocking bug against https://crbug.com/675631 and flag your
106use of base::CreateSingleThreadTaskRunnerWithTraits() with a TODO against your
107bug to use base::CreateSequencedTaskRunnerWithTraits() when fixed).
108
Gabriel Charette01567ac2017-06-09 15:31:10109Detailed documentation on how to migrate from single-threaded contexts to
Gabriel Charette8917f4c2018-11-22 15:50:28110sequenced contexts can be found [here](threading_and_tasks_faq.md#How-to-migrate-from-SingleThreadTaskRunner-to-SequencedTaskRunner).
Gabriel Charette01567ac2017-06-09 15:31:10111
gab2a4576052017-06-07 23:36:12112The discussion below covers all of these ways to execute tasks in details.
fdoraybacba4a22017-05-10 21:10:00113
114## Posting a Parallel Task
115
116### Direct Posting to the Task Scheduler
117
118A task that can run on any thread and doesn’t have ordering or mutual exclusion
119requirements with other tasks should be posted using one of the
120`base::PostTask*()` functions defined in
Gabriel Charette04b138f2018-08-06 00:03:22121[`base/task/post_task.h`](https://cs.chromium.org/chromium/src/base/task/post_task.h).
fdoraybacba4a22017-05-10 21:10:00122
123```cpp
124base::PostTask(FROM_HERE, base::BindOnce(&Task));
125```
126
127This posts tasks with default traits.
128
129The `base::PostTask*WithTraits()` functions allow the caller to provide
130additional details about the task via TaskTraits (ref.
131[Annotating Tasks with TaskTraits](#Annotating-Tasks-with-TaskTraits)).
132
133```cpp
134base::PostTaskWithTraits(
Gabriel Charetteb10aeebc2018-07-26 20:15:00135 FROM_HERE, {base::TaskPriority::BEST_EFFORT, MayBlock()},
fdoraybacba4a22017-05-10 21:10:00136 base::BindOnce(&Task));
137```
138
fdoray52bf5552017-05-11 12:43:59139### Posting via a TaskRunner
fdoraybacba4a22017-05-10 21:10:00140
141A parallel
142[`TaskRunner`](https://cs.chromium.org/chromium/src/base/task_runner.h) is an
143alternative to calling `base::PostTask*()` directly. This is mainly useful when
144it isn’t known in advance whether tasks will be posted in parallel, in sequence,
fdoray52bf5552017-05-11 12:43:59145or to a single-thread (ref.
146[Posting a Sequenced Task](#Posting-a-Sequenced-Task),
147[Posting Multiple Tasks to the Same Thread](#Posting-Multiple-Tasks-to-the-Same-Thread)).
148Since `TaskRunner` is the base class of `SequencedTaskRunner` and
149`SingleThreadTaskRunner`, a `scoped_refptr<TaskRunner>` member can hold a
fdoraybacba4a22017-05-10 21:10:00150`TaskRunner`, a `SequencedTaskRunner` or a `SingleThreadTaskRunner`.
151
152```cpp
153class A {
154 public:
155 A() = default;
156
157 void set_task_runner_for_testing(
158 scoped_refptr<base::TaskRunner> task_runner) {
159 task_runner_ = std::move(task_runner);
160 }
161
162 void DoSomething() {
163 // In production, A is always posted in parallel. In test, it is posted to
164 // the TaskRunner provided via set_task_runner_for_testing().
165 task_runner_->PostTask(FROM_HERE, base::BindOnce(&A));
166 }
167
168 private:
169 scoped_refptr<base::TaskRunner> task_runner_ =
170 base::CreateTaskRunnerWithTraits({base::TaskPriority::USER_VISIBLE});
171};
172```
173
174Unless a test needs to control precisely how tasks are executed, it is preferred
175to call `base::PostTask*()` directly (ref. [Testing](#Testing) for less invasive
176ways of controlling tasks in tests).
177
178## Posting a Sequenced Task
179
180A sequence is a set of tasks that run one at a time in posting order (not
181necessarily on the same thread). To post tasks as part of a sequence, use a
Gabriel Charette90480312018-02-16 15:10:05182[`SequencedTaskRunner`](https://cs.chromium.org/chromium/src/base/sequenced_task_runner.h).
fdoraybacba4a22017-05-10 21:10:00183
184### Posting to a New Sequence
185
186A `SequencedTaskRunner` can be created by
187`base::CreateSequencedTaskRunnerWithTraits()`.
188
189```cpp
190scoped_refptr<SequencedTaskRunner> sequenced_task_runner =
191 base::CreateSequencedTaskRunnerWithTraits(...);
192
193// TaskB runs after TaskA completes.
194sequenced_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskA));
195sequenced_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskB));
196```
197
198### Posting to the Current Sequence
199
200The `SequencedTaskRunner` to which the current task was posted can be obtained
201via
202[`SequencedTaskRunnerHandle::Get()`](https://cs.chromium.org/chromium/src/base/threading/sequenced_task_runner_handle.h).
203
204*** note
205**NOTE:** it is invalid to call `SequencedTaskRunnerHandle::Get()` from a
206parallel task, but it is valid from a single-threaded task (a
207`SingleThreadTaskRunner` is a `SequencedTaskRunner`).
208***
209
210```cpp
211// The task will run after any task that has already been posted
212// to the SequencedTaskRunner to which the current task was posted
213// (in particular, it will run after the current task completes).
214// It is also guaranteed that it won’t run concurrently with any
215// task posted to that SequencedTaskRunner.
216base::SequencedTaskRunnerHandle::Get()->
217 PostTask(FROM_HERE, base::BindOnce(&Task));
218```
219
220## Using Sequences Instead of Locks
221
222Usage of locks is discouraged in Chrome. Sequences inherently provide
Gabriel Charettea3ccc972018-11-13 14:43:12223thread-safety. Prefer classes that are always accessed from the same
224sequence to managing your own thread-safety with locks.
225
226**Thread-safe but not thread-affine; how so?** Tasks posted to the same sequence
227will run in sequential order. After a sequenced task completes, the next task
228may be picked up by a different worker thread, but that task is guaranteed to
229see any side-effects caused by the previous one(s) on its sequence.
fdoraybacba4a22017-05-10 21:10:00230
231```cpp
232class A {
233 public:
234 A() {
235 // Do not require accesses to be on the creation sequence.
isherman8c33b8a2017-06-27 19:18:30236 DETACH_FROM_SEQUENCE(sequence_checker_);
fdoraybacba4a22017-05-10 21:10:00237 }
238
239 void AddValue(int v) {
240 // Check that all accesses are on the same sequence.
isherman8c33b8a2017-06-27 19:18:30241 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
fdoraybacba4a22017-05-10 21:10:00242 values_.push_back(v);
243}
244
245 private:
isherman8c33b8a2017-06-27 19:18:30246 SEQUENCE_CHECKER(sequence_checker_);
fdoraybacba4a22017-05-10 21:10:00247
248 // No lock required, because all accesses are on the
249 // same sequence.
250 std::vector<int> values_;
251};
252
253A a;
254scoped_refptr<SequencedTaskRunner> task_runner_for_a = ...;
Mike Bjorged3a09842018-05-15 18:37:28255task_runner_for_a->PostTask(FROM_HERE,
256 base::BindOnce(&A::AddValue, base::Unretained(&a), 42));
257task_runner_for_a->PostTask(FROM_HERE,
258 base::BindOnce(&A::AddValue, base::Unretained(&a), 27));
fdoraybacba4a22017-05-10 21:10:00259
260// Access from a different sequence causes a DCHECK failure.
261scoped_refptr<SequencedTaskRunner> other_task_runner = ...;
262other_task_runner->PostTask(FROM_HERE,
Mike Bjorged3a09842018-05-15 18:37:28263 base::BindOnce(&A::AddValue, base::Unretained(&a), 1));
fdoraybacba4a22017-05-10 21:10:00264```
265
Gabriel Charette90480312018-02-16 15:10:05266Locks should only be used to swap in a shared data structure that can be
267accessed on multiple threads. If one thread updates it based on expensive
268computation or through disk access, then that slow work should be done without
269holding on to the lock. Only when the result is available should the lock be
270used to swap in the new data. An example of this is in PluginList::LoadPlugins
271([`content/common/plugin_list.cc`](https://cs.chromium.org/chromium/src/content/
272common/plugin_list.cc). If you must use locks,
273[here](https://www.chromium.org/developers/lock-and-condition-variable) are some
274best practices and pitfalls to avoid.
275
276In order to write non-blocking code, many APIs in Chromium are asynchronous.
277Usually this means that they either need to be executed on a particular
278thread/sequence and will return results via a custom delegate interface, or they
279take a `base::Callback<>` object that is called when the requested operation is
280completed. Executing work on a specific thread/sequence is covered in the
281PostTask sections above.
282
fdoraybacba4a22017-05-10 21:10:00283## Posting Multiple Tasks to the Same Thread
284
285If multiple tasks need to run on the same thread, post them to a
286[`SingleThreadTaskRunner`](https://cs.chromium.org/chromium/src/base/single_thread_task_runner.h).
287All tasks posted to the same `SingleThreadTaskRunner` run on the same thread in
288posting order.
289
290### Posting to the Main Thread or to the IO Thread in the Browser Process
291
Eric Seckler6cf08db82018-08-30 12:01:55292To post tasks to the main thread or to the IO thread, use
293`base::PostTaskWithTraits()` or get the appropriate SingleThreadTaskRunner using
294`base::CreateSingleThreadTaskRunnerWithTraits`, supplying a `BrowserThread::ID`
295as trait. For this, you'll also need to include
296[`content/public/browser/browser_task_traits.h`](https://cs.chromium.org/chromium/src/content/public/browser/browser_task_traits.h).
fdoraybacba4a22017-05-10 21:10:00297
298```cpp
Eric Seckler6cf08db82018-08-30 12:01:55299base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, ...);
fdoraybacba4a22017-05-10 21:10:00300
Eric Seckler6cf08db82018-08-30 12:01:55301base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO})
fdoraybacba4a22017-05-10 21:10:00302 ->PostTask(FROM_HERE, ...);
303```
304
305The main thread and the IO thread are already super busy. Therefore, prefer
fdoray52bf5552017-05-11 12:43:59306posting to a general purpose thread when possible (ref.
307[Posting a Parallel Task](#Posting-a-Parallel-Task),
308[Posting a Sequenced task](#Posting-a-Sequenced-Task)).
309Good reasons to post to the main thread are to update the UI or access objects
310that are bound to it (e.g. `Profile`). A good reason to post to the IO thread is
311to access the internals of components that are bound to it (e.g. IPCs, network).
312Note: It is not necessary to have an explicit post task to the IO thread to
313send/receive an IPC or send/receive data on the network.
fdoraybacba4a22017-05-10 21:10:00314
315### Posting to the Main Thread in a Renderer Process
316TODO
317
318### Posting to a Custom SingleThreadTaskRunner
319
320If multiple tasks need to run on the same thread and that thread doesn’t have to
321be the main thread or the IO thread, post them to a `SingleThreadTaskRunner`
322created by `base::CreateSingleThreadTaskRunnerWithTraits`.
323
324```cpp
325scoped_refptr<SequencedTaskRunner> single_thread_task_runner =
326 base::CreateSingleThreadTaskRunnerWithTraits(...);
327
328// TaskB runs after TaskA completes. Both tasks run on the same thread.
329single_thread_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskA));
330single_thread_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskB));
331```
332
333*** note
334**IMPORTANT:** You should rarely need this, most classes in Chromium require
335thread-safety (which sequences provide) not thread-affinity. If an API you’re
336using is incorrectly thread-affine (i.e. using
337[`base::ThreadChecker`](https://cs.chromium.org/chromium/src/base/threading/thread_checker.h)
338when it’s merely thread-unsafe and should use
339[`base::SequenceChecker`](https://cs.chromium.org/chromium/src/base/sequence_checker.h)),
Gabriel Charette41f4a482019-01-15 00:15:50340please consider
341[`fixing it`](threading_and_tasks_faq.md#How-to-migrate-from-SingleThreadTaskRunner-to-SequencedTaskRunner)
342instead of making things worse by also making your API thread-affine.
fdoraybacba4a22017-05-10 21:10:00343***
344
345### Posting to the Current Thread
346
347*** note
348**IMPORTANT:** To post a task that needs mutual exclusion with the current
349sequence of tasks but doesn’t absolutely need to run on the current thread, use
350`SequencedTaskRunnerHandle::Get()` instead of `ThreadTaskRunnerHandle::Get()`
351(ref. [Posting to the Current Sequence](#Posting-to-the-Current-Sequence)). That
352will better document the requirements of the posted task. In a single-thread
353task, `SequencedTaskRunnerHandle::Get()` is equivalent to
354`ThreadTaskRunnerHandle::Get()`.
355***
356
357To post a task to the current thread, use [`ThreadTaskRunnerHandle`](https://cs.chromium.org/chromium/src/base/threading/thread_task_runner_handle.h).
358
359```cpp
360// The task will run on the current thread in the future.
361base::ThreadTaskRunnerHandle::Get()->PostTask(
362 FROM_HERE, base::BindOnce(&Task));
363```
364
365*** note
366**NOTE:** It is invalid to call `ThreadTaskRunnerHandle::Get()` from a parallel
367or a sequenced task.
368***
369
370## Posting Tasks to a COM Single-Thread Apartment (STA) Thread (Windows)
371
372Tasks that need to run on a COM Single-Thread Apartment (STA) thread must be
373posted to a `SingleThreadTaskRunner` returned by
374`CreateCOMSTATaskRunnerWithTraits()`. As mentioned in [Posting Multiple Tasks to
375the Same Thread](#Posting-Multiple-Tasks-to-the-Same-Thread), all tasks posted
376to the same `SingleThreadTaskRunner` run on the same thread in posting order.
377
378```cpp
379// Task(A|B|C)UsingCOMSTA will run on the same COM STA thread.
380
381void TaskAUsingCOMSTA() {
382 // [ This runs on a COM STA thread. ]
383
384 // Make COM STA calls.
385 // ...
386
387 // Post another task to the current COM STA thread.
388 base::ThreadTaskRunnerHandle::Get()->PostTask(
389 FROM_HERE, base::BindOnce(&TaskCUsingCOMSTA));
390}
391void TaskBUsingCOMSTA() { }
392void TaskCUsingCOMSTA() { }
393
394auto com_sta_task_runner = base::CreateCOMSTATaskRunnerWithTraits(...);
395com_sta_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskAUsingCOMSTA));
396com_sta_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskBUsingCOMSTA));
397```
398
399## Annotating Tasks with TaskTraits
400
Gabriel Charette04b138f2018-08-06 00:03:22401[`TaskTraits`](https://cs.chromium.org/chromium/src/base/task/task_traits.h)
fdoraybacba4a22017-05-10 21:10:00402encapsulate information about a task that helps the task scheduler make better
403scheduling decisions.
404
405All `PostTask*()` functions in
Gabriel Charette04b138f2018-08-06 00:03:22406[`base/task/post_task.h`](https://cs.chromium.org/chromium/src/base/task/post_task.h)
fdoraybacba4a22017-05-10 21:10:00407have an overload that takes `TaskTraits` as argument and one that doesn’t. The
408overload that doesn’t take `TaskTraits` as argument is appropriate for tasks
409that:
410- Don’t block (ref. MayBlock and WithBaseSyncPrimitives).
411- Prefer inheriting the current priority to specifying their own.
412- Can either block shutdown or be skipped on shutdown (task scheduler is free to choose a fitting default).
413Tasks that don’t match this description must be posted with explicit TaskTraits.
414
Gabriel Charette04b138f2018-08-06 00:03:22415[`base/task/task_traits.h`](https://cs.chromium.org/chromium/src/base/task/task_traits.h)
Eric Seckler6cf08db82018-08-30 12:01:55416provides exhaustive documentation of available traits. The content layer also
417provides additional traits in
418[`content/public/browser/browser_task_traits.h`](https://cs.chromium.org/chromium/src/content/public/browser/browser_task_traits.h)
419to facilitate posting a task onto a BrowserThread.
420
421Below are some examples of how to specify `TaskTraits`.
fdoraybacba4a22017-05-10 21:10:00422
423```cpp
424// This task has no explicit TaskTraits. It cannot block. Its priority
425// is inherited from the calling context (e.g. if it is posted from
Gabriel Charette141a442582018-07-27 21:23:25426// a BEST_EFFORT task, it will have a BEST_EFFORT priority). It will either
fdoraybacba4a22017-05-10 21:10:00427// block shutdown or be skipped on shutdown.
428base::PostTask(FROM_HERE, base::BindOnce(...));
429
430// This task has the highest priority. The task scheduler will try to
Gabriel Charette141a442582018-07-27 21:23:25431// run it before USER_VISIBLE and BEST_EFFORT tasks.
fdoraybacba4a22017-05-10 21:10:00432base::PostTaskWithTraits(
433 FROM_HERE, {base::TaskPriority::USER_BLOCKING},
434 base::BindOnce(...));
435
436// This task has the lowest priority and is allowed to block (e.g. it
437// can read a file from disk).
438base::PostTaskWithTraits(
Gabriel Charetteb10aeebc2018-07-26 20:15:00439 FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
fdoraybacba4a22017-05-10 21:10:00440 base::BindOnce(...));
441
442// This task blocks shutdown. The process won't exit before its
443// execution is complete.
444base::PostTaskWithTraits(
445 FROM_HERE, {base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
446 base::BindOnce(...));
Eric Seckler6cf08db82018-08-30 12:01:55447
448// This task will run on the Browser UI thread.
449base::PostTaskWithTraits(
450 FROM_HERE, {content::BrowserThread::UI},
451 base::BindOnce(...));
fdoraybacba4a22017-05-10 21:10:00452```
453
454## Keeping the Browser Responsive
455
456Do not perform expensive work on the main thread, the IO thread or any sequence
457that is expected to run tasks with a low latency. Instead, perform expensive
458work asynchronously using `base::PostTaskAndReply*()` or
Gabriel Charette90480312018-02-16 15:10:05459`SequencedTaskRunner::PostTaskAndReply()`. Note that asynchronous/overlapped
460I/O on the IO thread are fine.
fdoraybacba4a22017-05-10 21:10:00461
462Example: Running the code below on the main thread will prevent the browser from
463responding to user input for a long time.
464
465```cpp
466// GetHistoryItemsFromDisk() may block for a long time.
467// AddHistoryItemsToOmniboxDropDown() updates the UI and therefore must
468// be called on the main thread.
469AddHistoryItemsToOmniboxDropdown(GetHistoryItemsFromDisk("keyword"));
470```
471
472The code below solves the problem by scheduling a call to
473`GetHistoryItemsFromDisk()` in a thread pool followed by a call to
474`AddHistoryItemsToOmniboxDropdown()` on the origin sequence (the main thread in
475this case). The return value of the first call is automatically provided as
476argument to the second call.
477
478```cpp
479base::PostTaskWithTraitsAndReplyWithResult(
480 FROM_HERE, {base::MayBlock()},
481 base::BindOnce(&GetHistoryItemsFromDisk, "keyword"),
482 base::BindOnce(&AddHistoryItemsToOmniboxDropdown));
483```
484
485## Posting a Task with a Delay
486
487### Posting a One-Off Task with a Delay
488
489To post a task that must run once after a delay expires, use
490`base::PostDelayedTask*()` or `TaskRunner::PostDelayedTask()`.
491
492```cpp
493base::PostDelayedTaskWithTraits(
Gabriel Charetteb10aeebc2018-07-26 20:15:00494 FROM_HERE, {base::TaskPriority::BEST_EFFORT}, base::BindOnce(&Task),
fdoraybacba4a22017-05-10 21:10:00495 base::TimeDelta::FromHours(1));
496
497scoped_refptr<base::SequencedTaskRunner> task_runner =
Gabriel Charetteb10aeebc2018-07-26 20:15:00498 base::CreateSequencedTaskRunnerWithTraits({base::TaskPriority::BEST_EFFORT});
fdoraybacba4a22017-05-10 21:10:00499task_runner->PostDelayedTask(
500 FROM_HERE, base::BindOnce(&Task), base::TimeDelta::FromHours(1));
501```
502
503*** note
504**NOTE:** A task that has a 1-hour delay probably doesn’t have to run right away
Gabriel Charetteb10aeebc2018-07-26 20:15:00505when its delay expires. Specify `base::TaskPriority::BEST_EFFORT` to prevent it
fdoraybacba4a22017-05-10 21:10:00506from slowing down the browser when its delay expires.
507***
508
509### Posting a Repeating Task with a Delay
510To post a task that must run at regular intervals,
511use [`base::RepeatingTimer`](https://cs.chromium.org/chromium/src/base/timer/timer.h).
512
513```cpp
514class A {
515 public:
516 ~A() {
517 // The timer is stopped automatically when it is deleted.
518 }
519 void StartDoingStuff() {
520 timer_.Start(FROM_HERE, TimeDelta::FromSeconds(1),
521 this, &MyClass::DoStuff);
522 }
523 void StopDoingStuff() {
524 timer_.Stop();
525 }
526 private:
527 void DoStuff() {
528 // This method is called every second on the sequence that invoked
529 // StartDoingStuff().
530 }
531 base::RepeatingTimer timer_;
532};
533```
534
535## Cancelling a Task
536
537### Using base::WeakPtr
538
539[`base::WeakPtr`](https://cs.chromium.org/chromium/src/base/memory/weak_ptr.h)
540can be used to ensure that any callback bound to an object is canceled when that
541object is destroyed.
542
543```cpp
544int Compute() { … }
545
546class A {
547 public:
548 A() : weak_ptr_factory_(this) {}
549
550 void ComputeAndStore() {
551 // Schedule a call to Compute() in a thread pool followed by
552 // a call to A::Store() on the current sequence. The call to
553 // A::Store() is canceled when |weak_ptr_factory_| is destroyed.
554 // (guarantees that |this| will not be used-after-free).
555 base::PostTaskAndReplyWithResult(
556 FROM_HERE, base::BindOnce(&Compute),
557 base::BindOnce(&A::Store, weak_ptr_factory_.GetWeakPtr()));
558 }
559
560 private:
561 void Store(int value) { value_ = value; }
562
563 int value_;
564 base::WeakPtrFactory<A> weak_ptr_factory_;
565};
566```
567
568Note: `WeakPtr` is not thread-safe: `GetWeakPtr()`, `~WeakPtrFactory()`, and
569`Compute()` (bound to a `WeakPtr`) must all run on the same sequence.
570
571### Using base::CancelableTaskTracker
572
573[`base::CancelableTaskTracker`](https://cs.chromium.org/chromium/src/base/task/cancelable_task_tracker.h)
574allows cancellation to happen on a different sequence than the one on which
575tasks run. Keep in mind that `CancelableTaskTracker` cannot cancel tasks that
576have already started to run.
577
578```cpp
579auto task_runner = base::CreateTaskRunnerWithTraits(base::TaskTraits());
580base::CancelableTaskTracker cancelable_task_tracker;
581cancelable_task_tracker.PostTask(task_runner.get(), FROM_HERE,
Peter Kasting341e1fb2018-02-24 00:03:01582 base::DoNothing());
fdoraybacba4a22017-05-10 21:10:00583// Cancels Task(), only if it hasn't already started running.
584cancelable_task_tracker.TryCancelAll();
585```
586
587## Testing
588
589To test code that uses `base::ThreadTaskRunnerHandle`,
590`base::SequencedTaskRunnerHandle` or a function in
Gabriel Charette04b138f2018-08-06 00:03:22591[`base/task/post_task.h`](https://cs.chromium.org/chromium/src/base/task/post_task.h), instantiate a
fdoraybacba4a22017-05-10 21:10:00592[`base::test::ScopedTaskEnvironment`](https://cs.chromium.org/chromium/src/base/test/scoped_task_environment.h)
593for the scope of the test.
594
Wezd9e4cb772019-01-09 03:07:03595Tests can run the ScopedTaskEnvironment's message pump using a RunLoop, which
596can be made to run until Quit, or to execute ready-to-run tasks and immediately
597return.
598
599ScopedTaskEnvironment configures RunLoop::Run() to LOG(FATAL) if it hasn't been
600explicitly quit after TestTimeouts::action_timeout(). This is preferable to
601having the test hang if the code under test fails to trigger the RunLoop to
602quit. The timeout can be overridden with ScopedRunTimeoutForTest.
603
fdoraybacba4a22017-05-10 21:10:00604```cpp
605class MyTest : public testing::Test {
606 public:
607 // ...
608 protected:
609 base::test::ScopedTaskEnvironment scoped_task_environment_;
610};
611
612TEST(MyTest, MyTest) {
613 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&A));
614 base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
615 base::BindOnce(&B));
616 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
617 FROM_HERE, base::BindOnce(&C), base::TimeDelta::Max());
618
619 // This runs the (Thread|Sequenced)TaskRunnerHandle queue until it is empty.
620 // Delayed tasks are not added to the queue until they are ripe for execution.
621 base::RunLoop().RunUntilIdle();
622 // A and B have been executed. C is not ripe for execution yet.
623
624 base::RunLoop run_loop;
625 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&D));
626 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop.QuitClosure());
627 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&E));
628
629 // This runs the (Thread|Sequenced)TaskRunnerHandle queue until QuitClosure is
630 // invoked.
631 run_loop.Run();
632 // D and run_loop.QuitClosure() have been executed. E is still in the queue.
633
634 // Tasks posted to task scheduler run asynchronously as they are posted.
635 base::PostTaskWithTraits(FROM_HERE, base::TaskTraits(), base::BindOnce(&F));
636 auto task_runner =
637 base::CreateSequencedTaskRunnerWithTraits(base::TaskTraits());
638 task_runner->PostTask(FROM_HERE, base::BindOnce(&G));
639
640 // To block until all tasks posted to task scheduler are done running:
641 base::TaskScheduler::GetInstance()->FlushForTesting();
642 // F and G have been executed.
643
644 base::PostTaskWithTraitsAndReplyWithResult(
645 FROM_HERE, base::TaskTrait(),
646 base::BindOnce(&H), base::BindOnce(&I));
647
648 // This runs the (Thread|Sequenced)TaskRunnerHandle queue until both the
649 // (Thread|Sequenced)TaskRunnerHandle queue and the TaskSchedule queue are
650 // empty:
651 scoped_task_environment_.RunUntilIdle();
652 // E, H, I have been executed.
653}
654```
655
fdoraybacba4a22017-05-10 21:10:00656## Using TaskScheduler in a New Process
657
658TaskScheduler needs to be initialized in a process before the functions in
Gabriel Charette04b138f2018-08-06 00:03:22659[`base/task/post_task.h`](https://cs.chromium.org/chromium/src/base/task/post_task.h)
fdoraybacba4a22017-05-10 21:10:00660can be used. Initialization of TaskScheduler in the Chrome browser process and
661child processes (renderer, GPU, utility) has already been taken care of. To use
662TaskScheduler in another process, initialize TaskScheduler early in the main
663function:
664
665```cpp
666// This initializes and starts TaskScheduler with default params.
667base::TaskScheduler::CreateAndStartWithDefaultParams(“process_name”);
Gabriel Charette04b138f2018-08-06 00:03:22668// The base/task/post_task.h API can now be used. Tasks will be // scheduled as
669// they are posted.
fdoraybacba4a22017-05-10 21:10:00670
671// This initializes TaskScheduler.
672base::TaskScheduler::Create(“process_name”);
Gabriel Charette04b138f2018-08-06 00:03:22673// The base/task/post_task.h API can now be used. No threads // will be created
674// and no tasks will be scheduled until after Start() is called.
fdoraybacba4a22017-05-10 21:10:00675base::TaskScheduler::GetInstance()->Start(params);
676// TaskScheduler can now create threads and schedule tasks.
677```
678
679And shutdown TaskScheduler late in the main function:
680
681```cpp
682base::TaskScheduler::GetInstance()->Shutdown();
683// Tasks posted with TaskShutdownBehavior::BLOCK_SHUTDOWN and
684// tasks posted with TaskShutdownBehavior::SKIP_ON_SHUTDOWN that
685// have started to run before the Shutdown() call have now completed their
686// execution. Tasks posted with
687// TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN may still be
688// running.
689```
Gabriel Charetteb86e5fe62017-06-08 19:39:28690## TaskRunner ownership (encourage no dependency injection)
Sebastien Marchandc95489b2017-05-25 16:39:34691
692TaskRunners shouldn't be passed through several components. Instead, the
693components that uses a TaskRunner should be the one that creates it.
694
695See [this example](https://codereview.chromium.org/2885173002/) of a
696refactoring where a TaskRunner was passed through a lot of components only to be
697used in an eventual leaf. The leaf can and should now obtain its TaskRunner
698directly from
Gabriel Charette04b138f2018-08-06 00:03:22699[`base/task/post_task.h`](https://cs.chromium.org/chromium/src/base/task/post_task.h).
Gabriel Charetteb86e5fe62017-06-08 19:39:28700
701Dependency injection of TaskRunners can still seldomly be useful to unit test a
702component when triggering a specific race in a specific way is essential to the
703test. For such cases the preferred approach is the following:
704
705```cpp
706class FooWithCustomizableTaskRunnerForTesting {
707 public:
708
709 void SetBackgroundTaskRunnerForTesting(
michaelpg12c04572017-06-26 23:25:06710 scoped_refptr<base::SequencedTaskRunner> background_task_runner);
Gabriel Charetteb86e5fe62017-06-08 19:39:28711
712 private:
michaelpg12c04572017-06-26 23:25:06713 scoped_refptr<base::SequencedTaskRunner> background_task_runner_ =
714 base::CreateSequencedTaskRunnerWithTraits(
Gabriel Charetteb10aeebc2018-07-26 20:15:00715 {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
Gabriel Charetteb86e5fe62017-06-08 19:39:28716}
717```
718
719Note that this still allows removing all layers of plumbing between //chrome and
720that component since unit tests will use the leaf layer directly.
Gabriel Charette8917f4c2018-11-22 15:50:28721
722## FAQ
723See [Threading and Tasks FAQ](threading_and_tasks_faq.md) for more examples.