blob: 9064e7e7d3d1f914e7cab1ae0c55391518312dce [file] [log] [blame] [view]
fdoraybacba4a22017-05-10 21:10:001# Threading and Tasks in Chrome
2
3[TOC]
4
5## Overview
6
Gabriel Charette90480312018-02-16 15:10:057Chromium is a very multithreaded product. We try to keep the UI as responsive as
8possible, and this means not blocking the UI thread with any blocking I/O or
9other expensive operations. Our approach is to use message passing as the way of
10communicating between threads. We discourage locking and threadsafe
11objects. Instead, objects live on only one thread, we pass messages between
12threads for communication, and we use callback interfaces (implemented by
13message passing) for most cross-thread requests.
14
fdoraybacba4a22017-05-10 21:10:0015### Threads
16
17Every Chrome process has
18
19* a main thread
20 * in the browser process: updates the UI
21 * in renderer processes: runs most of Blink
22* an IO thread
23 * in the browser process: handles IPCs and network requests
24 * in renderer processes: handles IPCs
25* a few more special-purpose threads
26* and a pool of general-purpose threads
27
28Most threads have a loop that gets tasks from a queue and runs them (the queue
29may be shared between multiple threads).
30
31### Tasks
32
33A task is a `base::OnceClosure` added to a queue for asynchronous execution.
34
35A `base::OnceClosure` stores a function pointer and arguments. It has a `Run()`
36method that invokes the function pointer using the bound arguments. It is
37created using `base::BindOnce`. (ref. [Callback<> and Bind()
38documentation](callback.md)).
39
40```
41void TaskA() {}
42void TaskB(int v) {}
43
44auto task_a = base::BindOnce(&TaskA);
45auto task_b = base::BindOnce(&TaskB, 42);
46```
47
48A group of tasks can be executed in one of the following ways:
49
50* [Parallel](#Posting-a-Parallel-Task): No task execution ordering, possibly all
51 at once on any thread
52* [Sequenced](#Posting-a-Sequenced-Task): Tasks executed in posting order, one
53 at a time on any thread.
54* [Single Threaded](#Posting-Multiple-Tasks-to-the-Same-Thread): Tasks executed
55 in posting order, one at a time on a single thread.
56 * [COM Single Threaded](#Posting-Tasks-to-a-COM-Single-Thread-Apartment-STA_Thread-Windows_):
57 A variant of single threaded with COM initialized.
58
gab2a4576052017-06-07 23:36:1259### Prefer Sequences to Threads
60
Gabriel Charetteb86e5fe62017-06-08 19:39:2861**Sequenced execution mode is far preferred to Single Threaded** in scenarios
gab2a4576052017-06-07 23:36:1262that require mere thread-safety as it opens up scheduling paradigms that
63wouldn't be possible otherwise (sequences can hop threads instead of being stuck
64behind unrelated work on a dedicated thread). Ability to hop threads also means
65the thread count can dynamically adapt to the machine's true resource
Gabriel Charette90480312018-02-16 15:10:0566availability (increased parallelism on bigger machines, avoids trashing
67resources on smaller machines).
gab2a4576052017-06-07 23:36:1268
69Many core APIs were recently made sequence-friendly (classes are rarely
70thread-affine -- i.e. only when using thread-local-storage or third-party APIs
71that do). But the codebase has long evolved assuming single-threaded contexts...
72If your class could run on a sequence but is blocked by an overzealous use of
73ThreadChecker/ThreadTaskRunnerHandle/SingleThreadTaskRunner in a leaf
74dependency, consider fixing that dependency for everyone's benefit (or at the
75very least file a blocking bug against https://crbug.com/675631 and flag your
76use of base::CreateSingleThreadTaskRunnerWithTraits() with a TODO against your
77bug to use base::CreateSequencedTaskRunnerWithTraits() when fixed).
78
Gabriel Charette01567ac2017-06-09 15:31:1079Detailed documentation on how to migrate from single-threaded contexts to
Francois Doraycc49b74d2018-10-09 13:49:0980sequenced contexts can be found [here](threading_and_tasks_faq.md#How-can-I-migrate-from-SingleThreadTaskRunner-to-SequencedTaskRunner_).
Gabriel Charette01567ac2017-06-09 15:31:1081
gab2a4576052017-06-07 23:36:1282The discussion below covers all of these ways to execute tasks in details.
fdoraybacba4a22017-05-10 21:10:0083
84## Posting a Parallel Task
85
86### Direct Posting to the Task Scheduler
87
88A task that can run on any thread and doesn’t have ordering or mutual exclusion
89requirements with other tasks should be posted using one of the
90`base::PostTask*()` functions defined in
Gabriel Charette04b138f2018-08-06 00:03:2291[`base/task/post_task.h`](https://cs.chromium.org/chromium/src/base/task/post_task.h).
fdoraybacba4a22017-05-10 21:10:0092
93```cpp
94base::PostTask(FROM_HERE, base::BindOnce(&Task));
95```
96
97This posts tasks with default traits.
98
99The `base::PostTask*WithTraits()` functions allow the caller to provide
100additional details about the task via TaskTraits (ref.
101[Annotating Tasks with TaskTraits](#Annotating-Tasks-with-TaskTraits)).
102
103```cpp
104base::PostTaskWithTraits(
Gabriel Charetteb10aeebc2018-07-26 20:15:00105 FROM_HERE, {base::TaskPriority::BEST_EFFORT, MayBlock()},
fdoraybacba4a22017-05-10 21:10:00106 base::BindOnce(&Task));
107```
108
fdoray52bf5552017-05-11 12:43:59109### Posting via a TaskRunner
fdoraybacba4a22017-05-10 21:10:00110
111A parallel
112[`TaskRunner`](https://cs.chromium.org/chromium/src/base/task_runner.h) is an
113alternative to calling `base::PostTask*()` directly. This is mainly useful when
114it isn’t known in advance whether tasks will be posted in parallel, in sequence,
fdoray52bf5552017-05-11 12:43:59115or to a single-thread (ref.
116[Posting a Sequenced Task](#Posting-a-Sequenced-Task),
117[Posting Multiple Tasks to the Same Thread](#Posting-Multiple-Tasks-to-the-Same-Thread)).
118Since `TaskRunner` is the base class of `SequencedTaskRunner` and
119`SingleThreadTaskRunner`, a `scoped_refptr<TaskRunner>` member can hold a
fdoraybacba4a22017-05-10 21:10:00120`TaskRunner`, a `SequencedTaskRunner` or a `SingleThreadTaskRunner`.
121
122```cpp
123class A {
124 public:
125 A() = default;
126
127 void set_task_runner_for_testing(
128 scoped_refptr<base::TaskRunner> task_runner) {
129 task_runner_ = std::move(task_runner);
130 }
131
132 void DoSomething() {
133 // In production, A is always posted in parallel. In test, it is posted to
134 // the TaskRunner provided via set_task_runner_for_testing().
135 task_runner_->PostTask(FROM_HERE, base::BindOnce(&A));
136 }
137
138 private:
139 scoped_refptr<base::TaskRunner> task_runner_ =
140 base::CreateTaskRunnerWithTraits({base::TaskPriority::USER_VISIBLE});
141};
142```
143
144Unless a test needs to control precisely how tasks are executed, it is preferred
145to call `base::PostTask*()` directly (ref. [Testing](#Testing) for less invasive
146ways of controlling tasks in tests).
147
148## Posting a Sequenced Task
149
150A sequence is a set of tasks that run one at a time in posting order (not
151necessarily on the same thread). To post tasks as part of a sequence, use a
Gabriel Charette90480312018-02-16 15:10:05152[`SequencedTaskRunner`](https://cs.chromium.org/chromium/src/base/sequenced_task_runner.h).
fdoraybacba4a22017-05-10 21:10:00153
154### Posting to a New Sequence
155
156A `SequencedTaskRunner` can be created by
157`base::CreateSequencedTaskRunnerWithTraits()`.
158
159```cpp
160scoped_refptr<SequencedTaskRunner> sequenced_task_runner =
161 base::CreateSequencedTaskRunnerWithTraits(...);
162
163// TaskB runs after TaskA completes.
164sequenced_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskA));
165sequenced_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskB));
166```
167
168### Posting to the Current Sequence
169
170The `SequencedTaskRunner` to which the current task was posted can be obtained
171via
172[`SequencedTaskRunnerHandle::Get()`](https://cs.chromium.org/chromium/src/base/threading/sequenced_task_runner_handle.h).
173
174*** note
175**NOTE:** it is invalid to call `SequencedTaskRunnerHandle::Get()` from a
176parallel task, but it is valid from a single-threaded task (a
177`SingleThreadTaskRunner` is a `SequencedTaskRunner`).
178***
179
180```cpp
181// The task will run after any task that has already been posted
182// to the SequencedTaskRunner to which the current task was posted
183// (in particular, it will run after the current task completes).
184// It is also guaranteed that it won’t run concurrently with any
185// task posted to that SequencedTaskRunner.
186base::SequencedTaskRunnerHandle::Get()->
187 PostTask(FROM_HERE, base::BindOnce(&Task));
188```
189
190## Using Sequences Instead of Locks
191
192Usage of locks is discouraged in Chrome. Sequences inherently provide
Gabriel Charettea3ccc972018-11-13 14:43:12193thread-safety. Prefer classes that are always accessed from the same
194sequence to managing your own thread-safety with locks.
195
196**Thread-safe but not thread-affine; how so?** Tasks posted to the same sequence
197will run in sequential order. After a sequenced task completes, the next task
198may be picked up by a different worker thread, but that task is guaranteed to
199see any side-effects caused by the previous one(s) on its sequence.
fdoraybacba4a22017-05-10 21:10:00200
201```cpp
202class A {
203 public:
204 A() {
205 // Do not require accesses to be on the creation sequence.
isherman8c33b8a2017-06-27 19:18:30206 DETACH_FROM_SEQUENCE(sequence_checker_);
fdoraybacba4a22017-05-10 21:10:00207 }
208
209 void AddValue(int v) {
210 // Check that all accesses are on the same sequence.
isherman8c33b8a2017-06-27 19:18:30211 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
fdoraybacba4a22017-05-10 21:10:00212 values_.push_back(v);
213}
214
215 private:
isherman8c33b8a2017-06-27 19:18:30216 SEQUENCE_CHECKER(sequence_checker_);
fdoraybacba4a22017-05-10 21:10:00217
218 // No lock required, because all accesses are on the
219 // same sequence.
220 std::vector<int> values_;
221};
222
223A a;
224scoped_refptr<SequencedTaskRunner> task_runner_for_a = ...;
Mike Bjorged3a09842018-05-15 18:37:28225task_runner_for_a->PostTask(FROM_HERE,
226 base::BindOnce(&A::AddValue, base::Unretained(&a), 42));
227task_runner_for_a->PostTask(FROM_HERE,
228 base::BindOnce(&A::AddValue, base::Unretained(&a), 27));
fdoraybacba4a22017-05-10 21:10:00229
230// Access from a different sequence causes a DCHECK failure.
231scoped_refptr<SequencedTaskRunner> other_task_runner = ...;
232other_task_runner->PostTask(FROM_HERE,
Mike Bjorged3a09842018-05-15 18:37:28233 base::BindOnce(&A::AddValue, base::Unretained(&a), 1));
fdoraybacba4a22017-05-10 21:10:00234```
235
Gabriel Charette90480312018-02-16 15:10:05236Locks should only be used to swap in a shared data structure that can be
237accessed on multiple threads. If one thread updates it based on expensive
238computation or through disk access, then that slow work should be done without
239holding on to the lock. Only when the result is available should the lock be
240used to swap in the new data. An example of this is in PluginList::LoadPlugins
241([`content/common/plugin_list.cc`](https://cs.chromium.org/chromium/src/content/
242common/plugin_list.cc). If you must use locks,
243[here](https://www.chromium.org/developers/lock-and-condition-variable) are some
244best practices and pitfalls to avoid.
245
246In order to write non-blocking code, many APIs in Chromium are asynchronous.
247Usually this means that they either need to be executed on a particular
248thread/sequence and will return results via a custom delegate interface, or they
249take a `base::Callback<>` object that is called when the requested operation is
250completed. Executing work on a specific thread/sequence is covered in the
251PostTask sections above.
252
fdoraybacba4a22017-05-10 21:10:00253## Posting Multiple Tasks to the Same Thread
254
255If multiple tasks need to run on the same thread, post them to a
256[`SingleThreadTaskRunner`](https://cs.chromium.org/chromium/src/base/single_thread_task_runner.h).
257All tasks posted to the same `SingleThreadTaskRunner` run on the same thread in
258posting order.
259
260### Posting to the Main Thread or to the IO Thread in the Browser Process
261
Eric Seckler6cf08db82018-08-30 12:01:55262To post tasks to the main thread or to the IO thread, use
263`base::PostTaskWithTraits()` or get the appropriate SingleThreadTaskRunner using
264`base::CreateSingleThreadTaskRunnerWithTraits`, supplying a `BrowserThread::ID`
265as trait. For this, you'll also need to include
266[`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:00267
268```cpp
Eric Seckler6cf08db82018-08-30 12:01:55269base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, ...);
fdoraybacba4a22017-05-10 21:10:00270
Eric Seckler6cf08db82018-08-30 12:01:55271base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO})
fdoraybacba4a22017-05-10 21:10:00272 ->PostTask(FROM_HERE, ...);
273```
274
275The main thread and the IO thread are already super busy. Therefore, prefer
fdoray52bf5552017-05-11 12:43:59276posting to a general purpose thread when possible (ref.
277[Posting a Parallel Task](#Posting-a-Parallel-Task),
278[Posting a Sequenced task](#Posting-a-Sequenced-Task)).
279Good reasons to post to the main thread are to update the UI or access objects
280that are bound to it (e.g. `Profile`). A good reason to post to the IO thread is
281to access the internals of components that are bound to it (e.g. IPCs, network).
282Note: It is not necessary to have an explicit post task to the IO thread to
283send/receive an IPC or send/receive data on the network.
fdoraybacba4a22017-05-10 21:10:00284
285### Posting to the Main Thread in a Renderer Process
286TODO
287
288### Posting to a Custom SingleThreadTaskRunner
289
290If multiple tasks need to run on the same thread and that thread doesn’t have to
291be the main thread or the IO thread, post them to a `SingleThreadTaskRunner`
292created by `base::CreateSingleThreadTaskRunnerWithTraits`.
293
294```cpp
295scoped_refptr<SequencedTaskRunner> single_thread_task_runner =
296 base::CreateSingleThreadTaskRunnerWithTraits(...);
297
298// TaskB runs after TaskA completes. Both tasks run on the same thread.
299single_thread_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskA));
300single_thread_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskB));
301```
302
303*** note
304**IMPORTANT:** You should rarely need this, most classes in Chromium require
305thread-safety (which sequences provide) not thread-affinity. If an API you’re
306using is incorrectly thread-affine (i.e. using
307[`base::ThreadChecker`](https://cs.chromium.org/chromium/src/base/threading/thread_checker.h)
308when it’s merely thread-unsafe and should use
309[`base::SequenceChecker`](https://cs.chromium.org/chromium/src/base/sequence_checker.h)),
310please consider fixing it instead of making things worse by also making your API thread-affine.
311***
312
313### Posting to the Current Thread
314
315*** note
316**IMPORTANT:** To post a task that needs mutual exclusion with the current
317sequence of tasks but doesn’t absolutely need to run on the current thread, use
318`SequencedTaskRunnerHandle::Get()` instead of `ThreadTaskRunnerHandle::Get()`
319(ref. [Posting to the Current Sequence](#Posting-to-the-Current-Sequence)). That
320will better document the requirements of the posted task. In a single-thread
321task, `SequencedTaskRunnerHandle::Get()` is equivalent to
322`ThreadTaskRunnerHandle::Get()`.
323***
324
325To post a task to the current thread, use [`ThreadTaskRunnerHandle`](https://cs.chromium.org/chromium/src/base/threading/thread_task_runner_handle.h).
326
327```cpp
328// The task will run on the current thread in the future.
329base::ThreadTaskRunnerHandle::Get()->PostTask(
330 FROM_HERE, base::BindOnce(&Task));
331```
332
333*** note
334**NOTE:** It is invalid to call `ThreadTaskRunnerHandle::Get()` from a parallel
335or a sequenced task.
336***
337
338## Posting Tasks to a COM Single-Thread Apartment (STA) Thread (Windows)
339
340Tasks that need to run on a COM Single-Thread Apartment (STA) thread must be
341posted to a `SingleThreadTaskRunner` returned by
342`CreateCOMSTATaskRunnerWithTraits()`. As mentioned in [Posting Multiple Tasks to
343the Same Thread](#Posting-Multiple-Tasks-to-the-Same-Thread), all tasks posted
344to the same `SingleThreadTaskRunner` run on the same thread in posting order.
345
346```cpp
347// Task(A|B|C)UsingCOMSTA will run on the same COM STA thread.
348
349void TaskAUsingCOMSTA() {
350 // [ This runs on a COM STA thread. ]
351
352 // Make COM STA calls.
353 // ...
354
355 // Post another task to the current COM STA thread.
356 base::ThreadTaskRunnerHandle::Get()->PostTask(
357 FROM_HERE, base::BindOnce(&TaskCUsingCOMSTA));
358}
359void TaskBUsingCOMSTA() { }
360void TaskCUsingCOMSTA() { }
361
362auto com_sta_task_runner = base::CreateCOMSTATaskRunnerWithTraits(...);
363com_sta_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskAUsingCOMSTA));
364com_sta_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskBUsingCOMSTA));
365```
366
367## Annotating Tasks with TaskTraits
368
Gabriel Charette04b138f2018-08-06 00:03:22369[`TaskTraits`](https://cs.chromium.org/chromium/src/base/task/task_traits.h)
fdoraybacba4a22017-05-10 21:10:00370encapsulate information about a task that helps the task scheduler make better
371scheduling decisions.
372
373All `PostTask*()` functions in
Gabriel Charette04b138f2018-08-06 00:03:22374[`base/task/post_task.h`](https://cs.chromium.org/chromium/src/base/task/post_task.h)
fdoraybacba4a22017-05-10 21:10:00375have an overload that takes `TaskTraits` as argument and one that doesn’t. The
376overload that doesn’t take `TaskTraits` as argument is appropriate for tasks
377that:
378- Don’t block (ref. MayBlock and WithBaseSyncPrimitives).
379- Prefer inheriting the current priority to specifying their own.
380- Can either block shutdown or be skipped on shutdown (task scheduler is free to choose a fitting default).
381Tasks that don’t match this description must be posted with explicit TaskTraits.
382
Gabriel Charette04b138f2018-08-06 00:03:22383[`base/task/task_traits.h`](https://cs.chromium.org/chromium/src/base/task/task_traits.h)
Eric Seckler6cf08db82018-08-30 12:01:55384provides exhaustive documentation of available traits. The content layer also
385provides additional traits in
386[`content/public/browser/browser_task_traits.h`](https://cs.chromium.org/chromium/src/content/public/browser/browser_task_traits.h)
387to facilitate posting a task onto a BrowserThread.
388
389Below are some examples of how to specify `TaskTraits`.
fdoraybacba4a22017-05-10 21:10:00390
391```cpp
392// This task has no explicit TaskTraits. It cannot block. Its priority
393// is inherited from the calling context (e.g. if it is posted from
Gabriel Charette141a442582018-07-27 21:23:25394// a BEST_EFFORT task, it will have a BEST_EFFORT priority). It will either
fdoraybacba4a22017-05-10 21:10:00395// block shutdown or be skipped on shutdown.
396base::PostTask(FROM_HERE, base::BindOnce(...));
397
398// This task has the highest priority. The task scheduler will try to
Gabriel Charette141a442582018-07-27 21:23:25399// run it before USER_VISIBLE and BEST_EFFORT tasks.
fdoraybacba4a22017-05-10 21:10:00400base::PostTaskWithTraits(
401 FROM_HERE, {base::TaskPriority::USER_BLOCKING},
402 base::BindOnce(...));
403
404// This task has the lowest priority and is allowed to block (e.g. it
405// can read a file from disk).
406base::PostTaskWithTraits(
Gabriel Charetteb10aeebc2018-07-26 20:15:00407 FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
fdoraybacba4a22017-05-10 21:10:00408 base::BindOnce(...));
409
410// This task blocks shutdown. The process won't exit before its
411// execution is complete.
412base::PostTaskWithTraits(
413 FROM_HERE, {base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
414 base::BindOnce(...));
Eric Seckler6cf08db82018-08-30 12:01:55415
416// This task will run on the Browser UI thread.
417base::PostTaskWithTraits(
418 FROM_HERE, {content::BrowserThread::UI},
419 base::BindOnce(...));
fdoraybacba4a22017-05-10 21:10:00420```
421
422## Keeping the Browser Responsive
423
424Do not perform expensive work on the main thread, the IO thread or any sequence
425that is expected to run tasks with a low latency. Instead, perform expensive
426work asynchronously using `base::PostTaskAndReply*()` or
Gabriel Charette90480312018-02-16 15:10:05427`SequencedTaskRunner::PostTaskAndReply()`. Note that asynchronous/overlapped
428I/O on the IO thread are fine.
fdoraybacba4a22017-05-10 21:10:00429
430Example: Running the code below on the main thread will prevent the browser from
431responding to user input for a long time.
432
433```cpp
434// GetHistoryItemsFromDisk() may block for a long time.
435// AddHistoryItemsToOmniboxDropDown() updates the UI and therefore must
436// be called on the main thread.
437AddHistoryItemsToOmniboxDropdown(GetHistoryItemsFromDisk("keyword"));
438```
439
440The code below solves the problem by scheduling a call to
441`GetHistoryItemsFromDisk()` in a thread pool followed by a call to
442`AddHistoryItemsToOmniboxDropdown()` on the origin sequence (the main thread in
443this case). The return value of the first call is automatically provided as
444argument to the second call.
445
446```cpp
447base::PostTaskWithTraitsAndReplyWithResult(
448 FROM_HERE, {base::MayBlock()},
449 base::BindOnce(&GetHistoryItemsFromDisk, "keyword"),
450 base::BindOnce(&AddHistoryItemsToOmniboxDropdown));
451```
452
453## Posting a Task with a Delay
454
455### Posting a One-Off Task with a Delay
456
457To post a task that must run once after a delay expires, use
458`base::PostDelayedTask*()` or `TaskRunner::PostDelayedTask()`.
459
460```cpp
461base::PostDelayedTaskWithTraits(
Gabriel Charetteb10aeebc2018-07-26 20:15:00462 FROM_HERE, {base::TaskPriority::BEST_EFFORT}, base::BindOnce(&Task),
fdoraybacba4a22017-05-10 21:10:00463 base::TimeDelta::FromHours(1));
464
465scoped_refptr<base::SequencedTaskRunner> task_runner =
Gabriel Charetteb10aeebc2018-07-26 20:15:00466 base::CreateSequencedTaskRunnerWithTraits({base::TaskPriority::BEST_EFFORT});
fdoraybacba4a22017-05-10 21:10:00467task_runner->PostDelayedTask(
468 FROM_HERE, base::BindOnce(&Task), base::TimeDelta::FromHours(1));
469```
470
471*** note
472**NOTE:** A task that has a 1-hour delay probably doesn’t have to run right away
Gabriel Charetteb10aeebc2018-07-26 20:15:00473when its delay expires. Specify `base::TaskPriority::BEST_EFFORT` to prevent it
fdoraybacba4a22017-05-10 21:10:00474from slowing down the browser when its delay expires.
475***
476
477### Posting a Repeating Task with a Delay
478To post a task that must run at regular intervals,
479use [`base::RepeatingTimer`](https://cs.chromium.org/chromium/src/base/timer/timer.h).
480
481```cpp
482class A {
483 public:
484 ~A() {
485 // The timer is stopped automatically when it is deleted.
486 }
487 void StartDoingStuff() {
488 timer_.Start(FROM_HERE, TimeDelta::FromSeconds(1),
489 this, &MyClass::DoStuff);
490 }
491 void StopDoingStuff() {
492 timer_.Stop();
493 }
494 private:
495 void DoStuff() {
496 // This method is called every second on the sequence that invoked
497 // StartDoingStuff().
498 }
499 base::RepeatingTimer timer_;
500};
501```
502
503## Cancelling a Task
504
505### Using base::WeakPtr
506
507[`base::WeakPtr`](https://cs.chromium.org/chromium/src/base/memory/weak_ptr.h)
508can be used to ensure that any callback bound to an object is canceled when that
509object is destroyed.
510
511```cpp
512int Compute() { … }
513
514class A {
515 public:
516 A() : weak_ptr_factory_(this) {}
517
518 void ComputeAndStore() {
519 // Schedule a call to Compute() in a thread pool followed by
520 // a call to A::Store() on the current sequence. The call to
521 // A::Store() is canceled when |weak_ptr_factory_| is destroyed.
522 // (guarantees that |this| will not be used-after-free).
523 base::PostTaskAndReplyWithResult(
524 FROM_HERE, base::BindOnce(&Compute),
525 base::BindOnce(&A::Store, weak_ptr_factory_.GetWeakPtr()));
526 }
527
528 private:
529 void Store(int value) { value_ = value; }
530
531 int value_;
532 base::WeakPtrFactory<A> weak_ptr_factory_;
533};
534```
535
536Note: `WeakPtr` is not thread-safe: `GetWeakPtr()`, `~WeakPtrFactory()`, and
537`Compute()` (bound to a `WeakPtr`) must all run on the same sequence.
538
539### Using base::CancelableTaskTracker
540
541[`base::CancelableTaskTracker`](https://cs.chromium.org/chromium/src/base/task/cancelable_task_tracker.h)
542allows cancellation to happen on a different sequence than the one on which
543tasks run. Keep in mind that `CancelableTaskTracker` cannot cancel tasks that
544have already started to run.
545
546```cpp
547auto task_runner = base::CreateTaskRunnerWithTraits(base::TaskTraits());
548base::CancelableTaskTracker cancelable_task_tracker;
549cancelable_task_tracker.PostTask(task_runner.get(), FROM_HERE,
Peter Kasting341e1fb2018-02-24 00:03:01550 base::DoNothing());
fdoraybacba4a22017-05-10 21:10:00551// Cancels Task(), only if it hasn't already started running.
552cancelable_task_tracker.TryCancelAll();
553```
554
555## Testing
556
557To test code that uses `base::ThreadTaskRunnerHandle`,
558`base::SequencedTaskRunnerHandle` or a function in
Gabriel Charette04b138f2018-08-06 00:03:22559[`base/task/post_task.h`](https://cs.chromium.org/chromium/src/base/task/post_task.h), instantiate a
fdoraybacba4a22017-05-10 21:10:00560[`base::test::ScopedTaskEnvironment`](https://cs.chromium.org/chromium/src/base/test/scoped_task_environment.h)
561for the scope of the test.
562
563```cpp
564class MyTest : public testing::Test {
565 public:
566 // ...
567 protected:
568 base::test::ScopedTaskEnvironment scoped_task_environment_;
569};
570
571TEST(MyTest, MyTest) {
572 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&A));
573 base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
574 base::BindOnce(&B));
575 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
576 FROM_HERE, base::BindOnce(&C), base::TimeDelta::Max());
577
578 // This runs the (Thread|Sequenced)TaskRunnerHandle queue until it is empty.
579 // Delayed tasks are not added to the queue until they are ripe for execution.
580 base::RunLoop().RunUntilIdle();
581 // A and B have been executed. C is not ripe for execution yet.
582
583 base::RunLoop run_loop;
584 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&D));
585 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop.QuitClosure());
586 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&E));
587
588 // This runs the (Thread|Sequenced)TaskRunnerHandle queue until QuitClosure is
589 // invoked.
590 run_loop.Run();
591 // D and run_loop.QuitClosure() have been executed. E is still in the queue.
592
593 // Tasks posted to task scheduler run asynchronously as they are posted.
594 base::PostTaskWithTraits(FROM_HERE, base::TaskTraits(), base::BindOnce(&F));
595 auto task_runner =
596 base::CreateSequencedTaskRunnerWithTraits(base::TaskTraits());
597 task_runner->PostTask(FROM_HERE, base::BindOnce(&G));
598
599 // To block until all tasks posted to task scheduler are done running:
600 base::TaskScheduler::GetInstance()->FlushForTesting();
601 // F and G have been executed.
602
603 base::PostTaskWithTraitsAndReplyWithResult(
604 FROM_HERE, base::TaskTrait(),
605 base::BindOnce(&H), base::BindOnce(&I));
606
607 // This runs the (Thread|Sequenced)TaskRunnerHandle queue until both the
608 // (Thread|Sequenced)TaskRunnerHandle queue and the TaskSchedule queue are
609 // empty:
610 scoped_task_environment_.RunUntilIdle();
611 // E, H, I have been executed.
612}
613```
614
fdoraybacba4a22017-05-10 21:10:00615## Using TaskScheduler in a New Process
616
617TaskScheduler needs to be initialized in a process before the functions in
Gabriel Charette04b138f2018-08-06 00:03:22618[`base/task/post_task.h`](https://cs.chromium.org/chromium/src/base/task/post_task.h)
fdoraybacba4a22017-05-10 21:10:00619can be used. Initialization of TaskScheduler in the Chrome browser process and
620child processes (renderer, GPU, utility) has already been taken care of. To use
621TaskScheduler in another process, initialize TaskScheduler early in the main
622function:
623
624```cpp
625// This initializes and starts TaskScheduler with default params.
626base::TaskScheduler::CreateAndStartWithDefaultParams(“process_name”);
Gabriel Charette04b138f2018-08-06 00:03:22627// The base/task/post_task.h API can now be used. Tasks will be // scheduled as
628// they are posted.
fdoraybacba4a22017-05-10 21:10:00629
630// This initializes TaskScheduler.
631base::TaskScheduler::Create(“process_name”);
Gabriel Charette04b138f2018-08-06 00:03:22632// The base/task/post_task.h API can now be used. No threads // will be created
633// and no tasks will be scheduled until after Start() is called.
fdoraybacba4a22017-05-10 21:10:00634base::TaskScheduler::GetInstance()->Start(params);
635// TaskScheduler can now create threads and schedule tasks.
636```
637
638And shutdown TaskScheduler late in the main function:
639
640```cpp
641base::TaskScheduler::GetInstance()->Shutdown();
642// Tasks posted with TaskShutdownBehavior::BLOCK_SHUTDOWN and
643// tasks posted with TaskShutdownBehavior::SKIP_ON_SHUTDOWN that
644// have started to run before the Shutdown() call have now completed their
645// execution. Tasks posted with
646// TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN may still be
647// running.
648```
Gabriel Charetteb86e5fe62017-06-08 19:39:28649## TaskRunner ownership (encourage no dependency injection)
Sebastien Marchandc95489b2017-05-25 16:39:34650
651TaskRunners shouldn't be passed through several components. Instead, the
652components that uses a TaskRunner should be the one that creates it.
653
654See [this example](https://codereview.chromium.org/2885173002/) of a
655refactoring where a TaskRunner was passed through a lot of components only to be
656used in an eventual leaf. The leaf can and should now obtain its TaskRunner
657directly from
Gabriel Charette04b138f2018-08-06 00:03:22658[`base/task/post_task.h`](https://cs.chromium.org/chromium/src/base/task/post_task.h).
Gabriel Charetteb86e5fe62017-06-08 19:39:28659
660Dependency injection of TaskRunners can still seldomly be useful to unit test a
661component when triggering a specific race in a specific way is essential to the
662test. For such cases the preferred approach is the following:
663
664```cpp
665class FooWithCustomizableTaskRunnerForTesting {
666 public:
667
668 void SetBackgroundTaskRunnerForTesting(
michaelpg12c04572017-06-26 23:25:06669 scoped_refptr<base::SequencedTaskRunner> background_task_runner);
Gabriel Charetteb86e5fe62017-06-08 19:39:28670
671 private:
michaelpg12c04572017-06-26 23:25:06672 scoped_refptr<base::SequencedTaskRunner> background_task_runner_ =
673 base::CreateSequencedTaskRunnerWithTraits(
Gabriel Charetteb10aeebc2018-07-26 20:15:00674 {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
Gabriel Charetteb86e5fe62017-06-08 19:39:28675}
676```
677
678Note that this still allows removing all layers of plumbing between //chrome and
679that component since unit tests will use the leaf layer directly.