# Threading and Tasks in Chrome - FAQ
[TOC]
Note: Make sure to read the main [Threading and Tasks](threading_and_tasks.md)
docs first.
## General
### On which thread will a task run?
A task is posted through the `base/task/thread_pool.h` API with `TaskTraits`.
* If `TaskTraits` contain `BrowserThread::UI`:
* The task runs on the main thread.
* If `TaskTraits` contain `BrowserThread::IO`:
* The task runs on the IO thread.
* If `TaskTraits` don't contain `BrowserThread::UI/IO`:
* If the task is posted through a `SingleThreadTaskRunner` obtained from
`CreateSingleThreadTaskRunner(..., mode)`:
* Where `mode` is `SingleThreadTaskRunnerThreadMode::DEDICATED`:
* The task runs on a thread that only runs tasks from that
SingleThreadTaskRunner. This is not the main thread nor the IO
thread.
* Where `mode` is `SingleThreadTaskRunnerThreadMode::SHARED`:
* The task runs on a thread that runs tasks from one or many
unrelated SingleThreadTaskRunners. This is not the main thread nor
the IO thread.
* Otherwise:
* The task runs in a thread pool.
As explained in [Prefer Sequences to Threads](threading_and_tasks.md#Prefer-Sequences-to-Threads),
tasks should generally run on a sequence in a thread pool rather than on a
dedicated thread.
### Does release of a TaskRunner block on posted tasks?
Releasing a TaskRunner reference does not wait for tasks previously posted to
the TaskRunner to complete their execution. Tasks can run normally after the
last client reference to the TaskRunner to which they were posted has been
released and it can even be kept alive indefinitely through
`SequencedTaskRunner::GetCurrentDefault()` or
`SingleThreadTaskRunner::GetCurrentDefault()`.
If you want some state to be deleted only after all tasks currently posted to a
SequencedTaskRunner have run, store that state in a helper object and schedule
deletion of that helper object on the SequencedTaskRunner using
`base::OnTaskRunnerDeleter` after posting the last task. See
[example CL](https://crrev.com/c/1416271/15/chrome/browser/performance_monitor/system_monitor.h).
But be aware that any task posting back to its "current" sequence can enqueue
itself after that "last" task.
## Making blocking calls (which do not use the CPU)
### How to make a blocking call without preventing other tasks from being scheduled?
The steps depend on where the task runs (see [Where will a task run?](#On-what-thread-will-a-task-run_)).
If the task runs in a thread pool:
* Annotate the scope that may block with
`ScopedBlockingCall(BlockingType::MAY_BLOCK/WILL_BLOCK)`. A few milliseconds
after the annotated scope is entered, the capacity of the thread pool is
incremented. This ensures that your task doesn't reduce the number of tasks
that can run concurrently on the CPU. If the scope exits, the thread pool
capacity goes back to normal.
If the task runs on the main thread, the IO thread or a `SHARED
SingleThreadTaskRunner`:
* Blocking on one of these threads will cause breakages. Move your task to a
thread pool (or to a `DEDICATED SingleThreadTaskRunner` if necessary - see
[Prefer Sequences to Threads](threading_and_tasks.md#Prefer-Sequences-to-Threads)).
If the task runs on a `DEDICATED SingleThreadTaskRunner`:
* Annotate the scope that may block with
`ScopedBlockingCall(BlockingType::MAY_BLOCK/WILL_BLOCK)`. The annotation is a
no-op that documents the blocking behavior (and makes it pass assertions).
Tasks posted to the same `DEDICATED SingleThreadTaskRunner` won't run until
your blocking task returns (they will never run if the blocking task never
returns).
[base/threading/scoped_blocking_call.h](https://cs.chromium.org/chromium/src/base/threading/scoped_blocking_call.h)
explains the difference between `MAY_BLOCK` and `WILL_BLOCK` and gives
examples of blocking operations.
### How to make a blocking call that may never return without preventing other tasks from being scheduled?
If you can't avoid making a call to a third-party library that may block off-
CPU, follow recommendations in [How to make a blocking call without affecting
other tasks?](#How-to-make-a-blocking-call-without-affecting-other-tasks_).
This ensures that a current task doesn't prevent other tasks from running even
if it never returns.
Since tasks posted to the same sequence can't run concurrently, it is advisable
to run tasks that may block indefinitely in
[parallel](threading_and_tasks.md#posting-a-parallel-task) rather than in
[sequence](threading_and_tasks.md#posting-a-sequenced-task) (unless posting many
such tasks at which point sequencing can be a useful tool to prevent flooding).
### Do calls to blocking //base APIs need to be annotated with ScopedBlockingCall?
No. All blocking //base APIs (e.g. `base::ReadFileToString`, `base::File::Read`,
`base::SysInfo::AmountOfFreeDiskSpace`, `base::WaitableEvent::Wait`, etc.) have
their own internal annotations. See
[base/threading/scoped_blocking_call.h](https://cs.chromium.org/chromium/src/base/threading/scoped_blocking_call.h).
### Can multiple ScopedBlockingCall be nested for the purpose of documentation?
Nested `ScopedBlockingCall` are supported. Most of the time, the inner
ScopedBlockingCalls will no-op (the exception is `WILL_BLOCK` nested in `MAY_BLOCK`).
As such, it is permitted to add a ScopedBlockingCall in the scope where a function
that is already annotated is called for documentation purposes.:
```cpp
Data GetDataFromNetwork() {
ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
// Fetch data from network.
...
return data;
}
void ProcessDataFromNetwork() {
Data data;
{
// Document the blocking behavior with a ScopedBlockingCall.
// Permitted, but not required since GetDataFromNetwork() is itself annotated.
ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
data = GetDataFromNetwork();
}
CPUIntensiveProcessing(data);
}
```
However, CPU usage should always be minimal within the scope of
`ScopedBlockingCall`. See
[base/threading/scoped_blocking_call.h](https://cs.chromium.org/chromium/src/base/threading/scoped_blocking_call.h).
## Sequences
### How to migrate from SingleThreadTaskRunner to SequencedTaskRunner?
The following mappings can be useful when migrating code from a
`SingleThreadTaskRunner` to a `SequencedTaskRunner`:
* base::SingleThreadTaskRunner -> base::SequencedTaskRunner
* SingleThreadTaskRunner::BelongsToCurrentThread() -> SequencedTaskRunner::RunsTasksInCurrentSequence()
* base::SingleThreadTaskRunner::CurrentDefaultHandle ->
base::SequencedTaskRunnerHandle::CurrentDefaultHandle
* THREAD_CHECKER -> SEQUENCE_CHECKER
* base::ThreadLocalStorage::Slot -> base::SequenceLocalStorageSlot
* BrowserThread::DeleteOnThread -> base::OnTaskRunnerDeleter / base::RefCountedDeleteOnSequence
* BrowserMessageFilter::OverrideThreadForMessage() -> BrowserMessageFilter::OverrideTaskRunnerForMessage()
* CreateSingleThreadTaskRunner() -> CreateSequencedTaskRunner()
* Every CreateSingleThreadTaskRunner() usage, outside of
BrowserThread::UI/IO, should be accompanied with a comment and ideally a
bug to make it sequence when the sequence-unfriendly dependency is
addressed.
### How to ensure mutual exclusion between tasks posted by a component?
Create a `SequencedTaskRunner` using `CreateSequencedTaskRunner()` and
store it on an object that can be accessed from all the PostTask() call sites
that require mutual exclusion. If there isn't a shared object that can own a
common `SequencedTaskRunner`, use
`Lazy(Sequenced|SingleThread|COMSTA)TaskRunner` in an anonymous namespace.
## Tests
### How to test code that posts tasks?
If the test uses `BrowserThread::UI/IO`, instantiate a
`content::BrowserTaskEnvironment` for the scope of the test. Call
`BrowserTaskEnvironment::RunUntilIdle()` to wait until all tasks have run.
If the test doesn't use `BrowserThread::UI/IO`, instantiate a
`base::test::TaskEnvironment` for the scope of the test. Call
`base::test::TaskEnvironment::RunUntilIdle()` to wait until all tasks have
run.
In both cases, you can run tasks until a condition is met. A test that waits for
a condition to be met is easier to understand and debug than a test that waits
for all tasks to run.
```cpp
int g_condition = false;
base::RunLoop run_loop;
base::ThreadPool::PostTask(FROM_HERE, {}, base::BindOnce(
[] (base::OnceClosure closure) {
g_condition = true;
std::move(quit_closure).Run();
}, run_loop.QuitClosure()));
// Runs tasks until the quit closure is invoked.
run_loop.Run();
EXPECT_TRUE(g_condition);
```
## Your question hasn't been answered?
1. Check the main [Threading and Tasks](threading_and_tasks.md) docs.
2. Ping
[[email protected]](https://groups.google.com/a/chromium.org/forum/#!forum/scheduler-dev).