blob: 223e39890e31ec59db059abbcbc05468519ad44b [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
80sequenced contexts can be found [here](task_scheduler_migration.md).
81
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
91[`base/task_scheduler/post_task.h`](https://cs.chromium.org/chromium/src/base/task_scheduler/post_task.h).
92
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 Charetteb10aeeb2018-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
193thread-safety. Prefer classes that are always accessed from the same sequence to
194managing your own thread-safety with locks.
195
196```cpp
197class A {
198 public:
199 A() {
200 // Do not require accesses to be on the creation sequence.
isherman8c33b8a2017-06-27 19:18:30201 DETACH_FROM_SEQUENCE(sequence_checker_);
fdoraybacba4a22017-05-10 21:10:00202 }
203
204 void AddValue(int v) {
205 // Check that all accesses are on the same sequence.
isherman8c33b8a2017-06-27 19:18:30206 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
fdoraybacba4a22017-05-10 21:10:00207 values_.push_back(v);
208}
209
210 private:
isherman8c33b8a2017-06-27 19:18:30211 SEQUENCE_CHECKER(sequence_checker_);
fdoraybacba4a22017-05-10 21:10:00212
213 // No lock required, because all accesses are on the
214 // same sequence.
215 std::vector<int> values_;
216};
217
218A a;
219scoped_refptr<SequencedTaskRunner> task_runner_for_a = ...;
Mike Bjorged3a09842018-05-15 18:37:28220task_runner_for_a->PostTask(FROM_HERE,
221 base::BindOnce(&A::AddValue, base::Unretained(&a), 42));
222task_runner_for_a->PostTask(FROM_HERE,
223 base::BindOnce(&A::AddValue, base::Unretained(&a), 27));
fdoraybacba4a22017-05-10 21:10:00224
225// Access from a different sequence causes a DCHECK failure.
226scoped_refptr<SequencedTaskRunner> other_task_runner = ...;
227other_task_runner->PostTask(FROM_HERE,
Mike Bjorged3a09842018-05-15 18:37:28228 base::BindOnce(&A::AddValue, base::Unretained(&a), 1));
fdoraybacba4a22017-05-10 21:10:00229```
230
Gabriel Charette90480312018-02-16 15:10:05231Locks should only be used to swap in a shared data structure that can be
232accessed on multiple threads. If one thread updates it based on expensive
233computation or through disk access, then that slow work should be done without
234holding on to the lock. Only when the result is available should the lock be
235used to swap in the new data. An example of this is in PluginList::LoadPlugins
236([`content/common/plugin_list.cc`](https://cs.chromium.org/chromium/src/content/
237common/plugin_list.cc). If you must use locks,
238[here](https://www.chromium.org/developers/lock-and-condition-variable) are some
239best practices and pitfalls to avoid.
240
241In order to write non-blocking code, many APIs in Chromium are asynchronous.
242Usually this means that they either need to be executed on a particular
243thread/sequence and will return results via a custom delegate interface, or they
244take a `base::Callback<>` object that is called when the requested operation is
245completed. Executing work on a specific thread/sequence is covered in the
246PostTask sections above.
247
fdoraybacba4a22017-05-10 21:10:00248## Posting Multiple Tasks to the Same Thread
249
250If multiple tasks need to run on the same thread, post them to a
251[`SingleThreadTaskRunner`](https://cs.chromium.org/chromium/src/base/single_thread_task_runner.h).
252All tasks posted to the same `SingleThreadTaskRunner` run on the same thread in
253posting order.
254
255### Posting to the Main Thread or to the IO Thread in the Browser Process
256
257To post tasks to the main thread or to the IO thread, get the appropriate
258SingleThreadTaskRunner using `content::BrowserThread::GetTaskRunnerForThread`.
259
260```cpp
261content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::UI)
262 ->PostTask(FROM_HERE, ...);
263
264content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO)
265 ->PostTask(FROM_HERE, ...);
266```
267
268The main thread and the IO thread are already super busy. Therefore, prefer
fdoray52bf5552017-05-11 12:43:59269posting to a general purpose thread when possible (ref.
270[Posting a Parallel Task](#Posting-a-Parallel-Task),
271[Posting a Sequenced task](#Posting-a-Sequenced-Task)).
272Good reasons to post to the main thread are to update the UI or access objects
273that are bound to it (e.g. `Profile`). A good reason to post to the IO thread is
274to access the internals of components that are bound to it (e.g. IPCs, network).
275Note: It is not necessary to have an explicit post task to the IO thread to
276send/receive an IPC or send/receive data on the network.
fdoraybacba4a22017-05-10 21:10:00277
278### Posting to the Main Thread in a Renderer Process
279TODO
280
281### Posting to a Custom SingleThreadTaskRunner
282
283If multiple tasks need to run on the same thread and that thread doesn’t have to
284be the main thread or the IO thread, post them to a `SingleThreadTaskRunner`
285created by `base::CreateSingleThreadTaskRunnerWithTraits`.
286
287```cpp
288scoped_refptr<SequencedTaskRunner> single_thread_task_runner =
289 base::CreateSingleThreadTaskRunnerWithTraits(...);
290
291// TaskB runs after TaskA completes. Both tasks run on the same thread.
292single_thread_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskA));
293single_thread_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskB));
294```
295
296*** note
297**IMPORTANT:** You should rarely need this, most classes in Chromium require
298thread-safety (which sequences provide) not thread-affinity. If an API you’re
299using is incorrectly thread-affine (i.e. using
300[`base::ThreadChecker`](https://cs.chromium.org/chromium/src/base/threading/thread_checker.h)
301when it’s merely thread-unsafe and should use
302[`base::SequenceChecker`](https://cs.chromium.org/chromium/src/base/sequence_checker.h)),
303please consider fixing it instead of making things worse by also making your API thread-affine.
304***
305
306### Posting to the Current Thread
307
308*** note
309**IMPORTANT:** To post a task that needs mutual exclusion with the current
310sequence of tasks but doesn’t absolutely need to run on the current thread, use
311`SequencedTaskRunnerHandle::Get()` instead of `ThreadTaskRunnerHandle::Get()`
312(ref. [Posting to the Current Sequence](#Posting-to-the-Current-Sequence)). That
313will better document the requirements of the posted task. In a single-thread
314task, `SequencedTaskRunnerHandle::Get()` is equivalent to
315`ThreadTaskRunnerHandle::Get()`.
316***
317
318To post a task to the current thread, use [`ThreadTaskRunnerHandle`](https://cs.chromium.org/chromium/src/base/threading/thread_task_runner_handle.h).
319
320```cpp
321// The task will run on the current thread in the future.
322base::ThreadTaskRunnerHandle::Get()->PostTask(
323 FROM_HERE, base::BindOnce(&Task));
324```
325
326*** note
327**NOTE:** It is invalid to call `ThreadTaskRunnerHandle::Get()` from a parallel
328or a sequenced task.
329***
330
331## Posting Tasks to a COM Single-Thread Apartment (STA) Thread (Windows)
332
333Tasks that need to run on a COM Single-Thread Apartment (STA) thread must be
334posted to a `SingleThreadTaskRunner` returned by
335`CreateCOMSTATaskRunnerWithTraits()`. As mentioned in [Posting Multiple Tasks to
336the Same Thread](#Posting-Multiple-Tasks-to-the-Same-Thread), all tasks posted
337to the same `SingleThreadTaskRunner` run on the same thread in posting order.
338
339```cpp
340// Task(A|B|C)UsingCOMSTA will run on the same COM STA thread.
341
342void TaskAUsingCOMSTA() {
343 // [ This runs on a COM STA thread. ]
344
345 // Make COM STA calls.
346 // ...
347
348 // Post another task to the current COM STA thread.
349 base::ThreadTaskRunnerHandle::Get()->PostTask(
350 FROM_HERE, base::BindOnce(&TaskCUsingCOMSTA));
351}
352void TaskBUsingCOMSTA() { }
353void TaskCUsingCOMSTA() { }
354
355auto com_sta_task_runner = base::CreateCOMSTATaskRunnerWithTraits(...);
356com_sta_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskAUsingCOMSTA));
357com_sta_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskBUsingCOMSTA));
358```
359
360## Annotating Tasks with TaskTraits
361
362[`TaskTraits`](https://cs.chromium.org/chromium/src/base/task_scheduler/task_traits.h)
363encapsulate information about a task that helps the task scheduler make better
364scheduling decisions.
365
366All `PostTask*()` functions in
367[`base/task_scheduler/post_task.h`](https://cs.chromium.org/chromium/src/base/task_scheduler/post_task.h)
368have an overload that takes `TaskTraits` as argument and one that doesn’t. The
369overload that doesn’t take `TaskTraits` as argument is appropriate for tasks
370that:
371- Don’t block (ref. MayBlock and WithBaseSyncPrimitives).
372- Prefer inheriting the current priority to specifying their own.
373- Can either block shutdown or be skipped on shutdown (task scheduler is free to choose a fitting default).
374Tasks that don’t match this description must be posted with explicit TaskTraits.
375
376[`base/task_scheduler/task_traits.h`](https://cs.chromium.org/chromium/src/base/task_scheduler/task_traits.h)
377provides exhaustive documentation of available traits. Below are some examples
378of how to specify `TaskTraits`.
379
380```cpp
381// This task has no explicit TaskTraits. It cannot block. Its priority
382// is inherited from the calling context (e.g. if it is posted from
383// a BACKGROUND task, it will have a BACKGROUND priority). It will either
384// block shutdown or be skipped on shutdown.
385base::PostTask(FROM_HERE, base::BindOnce(...));
386
387// This task has the highest priority. The task scheduler will try to
388// run it before USER_VISIBLE and BACKGROUND tasks.
389base::PostTaskWithTraits(
390 FROM_HERE, {base::TaskPriority::USER_BLOCKING},
391 base::BindOnce(...));
392
393// This task has the lowest priority and is allowed to block (e.g. it
394// can read a file from disk).
395base::PostTaskWithTraits(
Gabriel Charetteb10aeeb2018-07-26 20:15:00396 FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
fdoraybacba4a22017-05-10 21:10:00397 base::BindOnce(...));
398
399// This task blocks shutdown. The process won't exit before its
400// execution is complete.
401base::PostTaskWithTraits(
402 FROM_HERE, {base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
403 base::BindOnce(...));
404```
405
406## Keeping the Browser Responsive
407
408Do not perform expensive work on the main thread, the IO thread or any sequence
409that is expected to run tasks with a low latency. Instead, perform expensive
410work asynchronously using `base::PostTaskAndReply*()` or
Gabriel Charette90480312018-02-16 15:10:05411`SequencedTaskRunner::PostTaskAndReply()`. Note that asynchronous/overlapped
412I/O on the IO thread are fine.
fdoraybacba4a22017-05-10 21:10:00413
414Example: Running the code below on the main thread will prevent the browser from
415responding to user input for a long time.
416
417```cpp
418// GetHistoryItemsFromDisk() may block for a long time.
419// AddHistoryItemsToOmniboxDropDown() updates the UI and therefore must
420// be called on the main thread.
421AddHistoryItemsToOmniboxDropdown(GetHistoryItemsFromDisk("keyword"));
422```
423
424The code below solves the problem by scheduling a call to
425`GetHistoryItemsFromDisk()` in a thread pool followed by a call to
426`AddHistoryItemsToOmniboxDropdown()` on the origin sequence (the main thread in
427this case). The return value of the first call is automatically provided as
428argument to the second call.
429
430```cpp
431base::PostTaskWithTraitsAndReplyWithResult(
432 FROM_HERE, {base::MayBlock()},
433 base::BindOnce(&GetHistoryItemsFromDisk, "keyword"),
434 base::BindOnce(&AddHistoryItemsToOmniboxDropdown));
435```
436
437## Posting a Task with a Delay
438
439### Posting a One-Off Task with a Delay
440
441To post a task that must run once after a delay expires, use
442`base::PostDelayedTask*()` or `TaskRunner::PostDelayedTask()`.
443
444```cpp
445base::PostDelayedTaskWithTraits(
Gabriel Charetteb10aeeb2018-07-26 20:15:00446 FROM_HERE, {base::TaskPriority::BEST_EFFORT}, base::BindOnce(&Task),
fdoraybacba4a22017-05-10 21:10:00447 base::TimeDelta::FromHours(1));
448
449scoped_refptr<base::SequencedTaskRunner> task_runner =
Gabriel Charetteb10aeeb2018-07-26 20:15:00450 base::CreateSequencedTaskRunnerWithTraits({base::TaskPriority::BEST_EFFORT});
fdoraybacba4a22017-05-10 21:10:00451task_runner->PostDelayedTask(
452 FROM_HERE, base::BindOnce(&Task), base::TimeDelta::FromHours(1));
453```
454
455*** note
456**NOTE:** A task that has a 1-hour delay probably doesn’t have to run right away
Gabriel Charetteb10aeeb2018-07-26 20:15:00457when its delay expires. Specify `base::TaskPriority::BEST_EFFORT` to prevent it
fdoraybacba4a22017-05-10 21:10:00458from slowing down the browser when its delay expires.
459***
460
461### Posting a Repeating Task with a Delay
462To post a task that must run at regular intervals,
463use [`base::RepeatingTimer`](https://cs.chromium.org/chromium/src/base/timer/timer.h).
464
465```cpp
466class A {
467 public:
468 ~A() {
469 // The timer is stopped automatically when it is deleted.
470 }
471 void StartDoingStuff() {
472 timer_.Start(FROM_HERE, TimeDelta::FromSeconds(1),
473 this, &MyClass::DoStuff);
474 }
475 void StopDoingStuff() {
476 timer_.Stop();
477 }
478 private:
479 void DoStuff() {
480 // This method is called every second on the sequence that invoked
481 // StartDoingStuff().
482 }
483 base::RepeatingTimer timer_;
484};
485```
486
487## Cancelling a Task
488
489### Using base::WeakPtr
490
491[`base::WeakPtr`](https://cs.chromium.org/chromium/src/base/memory/weak_ptr.h)
492can be used to ensure that any callback bound to an object is canceled when that
493object is destroyed.
494
495```cpp
496int Compute() { … }
497
498class A {
499 public:
500 A() : weak_ptr_factory_(this) {}
501
502 void ComputeAndStore() {
503 // Schedule a call to Compute() in a thread pool followed by
504 // a call to A::Store() on the current sequence. The call to
505 // A::Store() is canceled when |weak_ptr_factory_| is destroyed.
506 // (guarantees that |this| will not be used-after-free).
507 base::PostTaskAndReplyWithResult(
508 FROM_HERE, base::BindOnce(&Compute),
509 base::BindOnce(&A::Store, weak_ptr_factory_.GetWeakPtr()));
510 }
511
512 private:
513 void Store(int value) { value_ = value; }
514
515 int value_;
516 base::WeakPtrFactory<A> weak_ptr_factory_;
517};
518```
519
520Note: `WeakPtr` is not thread-safe: `GetWeakPtr()`, `~WeakPtrFactory()`, and
521`Compute()` (bound to a `WeakPtr`) must all run on the same sequence.
522
523### Using base::CancelableTaskTracker
524
525[`base::CancelableTaskTracker`](https://cs.chromium.org/chromium/src/base/task/cancelable_task_tracker.h)
526allows cancellation to happen on a different sequence than the one on which
527tasks run. Keep in mind that `CancelableTaskTracker` cannot cancel tasks that
528have already started to run.
529
530```cpp
531auto task_runner = base::CreateTaskRunnerWithTraits(base::TaskTraits());
532base::CancelableTaskTracker cancelable_task_tracker;
533cancelable_task_tracker.PostTask(task_runner.get(), FROM_HERE,
Peter Kasting341e1fb2018-02-24 00:03:01534 base::DoNothing());
fdoraybacba4a22017-05-10 21:10:00535// Cancels Task(), only if it hasn't already started running.
536cancelable_task_tracker.TryCancelAll();
537```
538
539## Testing
540
541To test code that uses `base::ThreadTaskRunnerHandle`,
542`base::SequencedTaskRunnerHandle` or a function in
543[`base/task_scheduler/post_task.h`](https://cs.chromium.org/chromium/src/base/task_scheduler/post_task.h), instantiate a
544[`base::test::ScopedTaskEnvironment`](https://cs.chromium.org/chromium/src/base/test/scoped_task_environment.h)
545for the scope of the test.
546
547```cpp
548class MyTest : public testing::Test {
549 public:
550 // ...
551 protected:
552 base::test::ScopedTaskEnvironment scoped_task_environment_;
553};
554
555TEST(MyTest, MyTest) {
556 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&A));
557 base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
558 base::BindOnce(&B));
559 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
560 FROM_HERE, base::BindOnce(&C), base::TimeDelta::Max());
561
562 // This runs the (Thread|Sequenced)TaskRunnerHandle queue until it is empty.
563 // Delayed tasks are not added to the queue until they are ripe for execution.
564 base::RunLoop().RunUntilIdle();
565 // A and B have been executed. C is not ripe for execution yet.
566
567 base::RunLoop run_loop;
568 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&D));
569 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop.QuitClosure());
570 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&E));
571
572 // This runs the (Thread|Sequenced)TaskRunnerHandle queue until QuitClosure is
573 // invoked.
574 run_loop.Run();
575 // D and run_loop.QuitClosure() have been executed. E is still in the queue.
576
577 // Tasks posted to task scheduler run asynchronously as they are posted.
578 base::PostTaskWithTraits(FROM_HERE, base::TaskTraits(), base::BindOnce(&F));
579 auto task_runner =
580 base::CreateSequencedTaskRunnerWithTraits(base::TaskTraits());
581 task_runner->PostTask(FROM_HERE, base::BindOnce(&G));
582
583 // To block until all tasks posted to task scheduler are done running:
584 base::TaskScheduler::GetInstance()->FlushForTesting();
585 // F and G have been executed.
586
587 base::PostTaskWithTraitsAndReplyWithResult(
588 FROM_HERE, base::TaskTrait(),
589 base::BindOnce(&H), base::BindOnce(&I));
590
591 // This runs the (Thread|Sequenced)TaskRunnerHandle queue until both the
592 // (Thread|Sequenced)TaskRunnerHandle queue and the TaskSchedule queue are
593 // empty:
594 scoped_task_environment_.RunUntilIdle();
595 // E, H, I have been executed.
596}
597```
598
fdoraybacba4a22017-05-10 21:10:00599## Using TaskScheduler in a New Process
600
601TaskScheduler needs to be initialized in a process before the functions in
602[`base/task_scheduler/post_task.h`](https://cs.chromium.org/chromium/src/base/task_scheduler/post_task.h)
603can be used. Initialization of TaskScheduler in the Chrome browser process and
604child processes (renderer, GPU, utility) has already been taken care of. To use
605TaskScheduler in another process, initialize TaskScheduler early in the main
606function:
607
608```cpp
609// This initializes and starts TaskScheduler with default params.
610base::TaskScheduler::CreateAndStartWithDefaultParams(“process_name”);
611// The base/task_scheduler/post_task.h API can now be used. Tasks will be
612// scheduled as they are posted.
613
614// This initializes TaskScheduler.
615base::TaskScheduler::Create(“process_name”);
616// The base/task_scheduler/post_task.h API can now be used. No threads
617// will be created and no tasks will be scheduled until after Start() is called.
618base::TaskScheduler::GetInstance()->Start(params);
619// TaskScheduler can now create threads and schedule tasks.
620```
621
622And shutdown TaskScheduler late in the main function:
623
624```cpp
625base::TaskScheduler::GetInstance()->Shutdown();
626// Tasks posted with TaskShutdownBehavior::BLOCK_SHUTDOWN and
627// tasks posted with TaskShutdownBehavior::SKIP_ON_SHUTDOWN that
628// have started to run before the Shutdown() call have now completed their
629// execution. Tasks posted with
630// TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN may still be
631// running.
632```
Gabriel Charetteb86e5fe62017-06-08 19:39:28633## TaskRunner ownership (encourage no dependency injection)
Sebastien Marchandc95489b2017-05-25 16:39:34634
635TaskRunners shouldn't be passed through several components. Instead, the
636components that uses a TaskRunner should be the one that creates it.
637
638See [this example](https://codereview.chromium.org/2885173002/) of a
639refactoring where a TaskRunner was passed through a lot of components only to be
640used in an eventual leaf. The leaf can and should now obtain its TaskRunner
641directly from
642[`base/task_scheduler/post_task.h`](https://cs.chromium.org/chromium/src/base/task_scheduler/post_task.h).
Gabriel Charetteb86e5fe62017-06-08 19:39:28643
644Dependency injection of TaskRunners can still seldomly be useful to unit test a
645component when triggering a specific race in a specific way is essential to the
646test. For such cases the preferred approach is the following:
647
648```cpp
649class FooWithCustomizableTaskRunnerForTesting {
650 public:
651
652 void SetBackgroundTaskRunnerForTesting(
michaelpg12c04572017-06-26 23:25:06653 scoped_refptr<base::SequencedTaskRunner> background_task_runner);
Gabriel Charetteb86e5fe62017-06-08 19:39:28654
655 private:
michaelpg12c04572017-06-26 23:25:06656 scoped_refptr<base::SequencedTaskRunner> background_task_runner_ =
657 base::CreateSequencedTaskRunnerWithTraits(
Gabriel Charetteb10aeeb2018-07-26 20:15:00658 {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
Gabriel Charetteb86e5fe62017-06-08 19:39:28659}
660```
661
662Note that this still allows removing all layers of plumbing between //chrome and
663that component since unit tests will use the leaf layer directly.