Francois Doray | cc49b74d | 2018-10-09 13:49:09 | [diff] [blame^] | 1 | # Threading and Tasks in Chrome - FAQ |
| 2 | |
| 3 | [TOC] |
| 4 | |
| 5 | ## General |
| 6 | |
| 7 | ### On what thread will a task run? |
| 8 | |
| 9 | A task is posted through the `base/task/post_task.h` API with `TaskTraits`. |
| 10 | |
| 11 | * If `TaskTraits` contain `BrowserThread::UI`: |
| 12 | * The task runs on the main thread. |
| 13 | |
| 14 | * If `TaskTraits` contain `BrowserThread::IO`: |
| 15 | * The task runs on the IO thread. |
| 16 | |
| 17 | * If `TaskTraits` don't contain `BrowserThread::UI/IO`: |
| 18 | * If the task is posted through a `SingleThreadTaskRunner` obtained from |
| 19 | `CreateSingleThreadTaskRunnerWithTraits(..., mode)`: |
| 20 | * Where `mode` is `SingleThreadTaskRunnerThreadMode::DEDICATED`: |
| 21 | * The task runs on a thread that only runs tasks from that |
| 22 | SingleThreadTaskRunner. This is not the main thread or the IO |
| 23 | thread. |
| 24 | |
| 25 | * Where `mode` is `SingleThreadTaskRunnerThreadMode::SHARED`: |
| 26 | * The task runs on a thread that runs tasks from one or many |
| 27 | unrelated SingleThreadTaskRunners. This is not the main thread |
| 28 | or the IO thread. |
| 29 | |
| 30 | * Otherwise: |
| 31 | * The task runs in a thread pool. |
| 32 | |
| 33 | As explained in [Prefer Sequences to Threads](threading_and_tasks.md#Prefer-Sequences-to-Threads), |
| 34 | tasks should generally run on a sequence in a thread pool rather than on a |
| 35 | dedicated thread. |
| 36 | |
| 37 | ## Blocking off-CPU |
| 38 | |
| 39 | ### How to make a call that may block off-CPU and never return? |
| 40 | |
| 41 | If you can't avoid making a call to a third-party library that may block off-CPU |
| 42 | and never return, you must take some steps to ensure that it doesn't prevent |
| 43 | other tasks from running. The steps depend on where the task runs (see [Where |
| 44 | will a task run?](#On-what-thread-will-a-task-run_)). |
| 45 | |
| 46 | If the task runs in a thread pool: |
| 47 | |
| 48 | * Annotate the scope that may block off-CPU with |
| 49 | `ScopedBlockingCall(BlockingType::MAY_BLOCK/WILL_BLOCK)`. A few milliseconds |
| 50 | after the annotated scope is entered, the capacity of the thread pool is |
| 51 | incremented. This ensures that your task doesn't reduce the number of tasks |
| 52 | that can run concurrently on the CPU. If the scope exits, the thread pool |
| 53 | capacity goes back to normal. Since tasks posted to the same sequence can't |
| 54 | run concurrently, it is advisable to run tasks that may block indefinitely in |
| 55 | [parallel](threading_and_tasks.md#posting-a-parallel-task) rather than in |
| 56 | [sequence](threading_and_tasks.md#posting-a-sequenced-task). |
| 57 | |
| 58 | If the task runs on the main thread, the IO thread or a `SHARED |
| 59 | SingleThreadTaskRunner`: |
| 60 | |
| 61 | * Blocking on one of these threads will cause breakages. Move your task to a |
| 62 | thread pool (or to a `DEDICATED SingleThreadTaskRunner` if necessary - see |
| 63 | [Prefer Sequences to Threads](threading_and_tasks.md#Prefer-Sequences-to-Threads)). |
| 64 | |
| 65 | If the task runs on a `DEDICATED SingleThreadTaskRunner`: |
| 66 | |
| 67 | * Annotate the scope that may block off-CPU with |
| 68 | `ScopedBlockingCall(BlockingType::MAY_BLOCK/WILL_BLOCK)`. The annotation is a |
| 69 | no-op that documents the blocking behavior. Tasks posted to the same |
| 70 | `DEDICATED SingleThreadTaskRunner` won't run until your blocking task returns |
| 71 | (they will never run if the blocking task never returns). |
| 72 | |
| 73 | `[base/threading/scoped_blocking_call.h](https://cs.chromium.org/chromium/src/base/threading/scoped_blocking_call.h)` |
| 74 | explains the difference between `MAY_BLOCK ` and `WILL_BLOCK` and gives |
| 75 | examples of off-CPU blocking operations. |
| 76 | |
| 77 | ## Sequences |
| 78 | |
| 79 | ### How to migrate from SingleThreadTaskRunner to SequencedTaskRunner? |
| 80 | |
| 81 | The following mappings can be useful when migrating code from a |
| 82 | `SingleThreadTaskRunner` to a `SequencedTaskRunner`: |
| 83 | |
| 84 | * base::SingleThreadTaskRunner -> base::SequencedTaskRunner |
| 85 | * SingleThreadTaskRunner::BelongsToCurrentThread() -> SequencedTaskRunner::RunsTasksInCurrentSequence() |
| 86 | * base::ThreadTaskRunnerHandle -> base::SequencedTaskRunnerHandle |
| 87 | * THREAD_CHECKER -> SEQUENCE_CHECKER |
| 88 | * base::ThreadLocalStorage::Slot -> base::SequenceLocalStorageSlot |
| 89 | * BrowserThread::DeleteOnThread -> base::OnTaskRunnerDeleter / base::RefCountedDeleteOnSequence |
| 90 | * BrowserMessageFilter::OverrideThreadForMessage() -> BrowserMessageFilter::OverrideTaskRunnerForMessage() |
| 91 | * CreateSingleThreadTaskRunnerWithTraits() -> CreateSequencedTaskRunnerWithTraits() |
| 92 | * Every CreateSingleThreadTaskRunnerWithTraits() usage should be accompanied |
| 93 | with a comment and ideally a bug to make it sequence when the sequence-unfriendly |
| 94 | dependency is addressed. |
| 95 | |
| 96 | ### How to ensure mutual exclusion between tasks posted by a component? |
| 97 | |
| 98 | Create a `SequencedTaskRunner` using `CreateSequencedTaskRunnerWithTraits()` and |
| 99 | store it on an object that can be accessed from all the PostTask() call sites |
| 100 | that require mutual exclusion. If there isn't a shared object that can own |
| 101 | common `SequencedTaskRunner`, use |
| 102 | `Lazy(Sequenced|SingleThread|COMSTA)TaskRunner` in an anonymous namespace. |
| 103 | |
| 104 | ## Tests |
| 105 | |
| 106 | ### How to test code that posts tasks? |
| 107 | |
| 108 | If the test uses `BrowserThread::UI/IO`, instantiate a |
| 109 | `content::TestBrowserThreadBundle` for the scope of the test. Call |
| 110 | `content::RunAllTasksUntilIdle()` to wait until all tasks have run. |
| 111 | |
| 112 | If the test doesn't use `BrowserThread::UI/IO`, instantiate a |
| 113 | `base::test::ScopedTaskEnvironment` for the scope of the test. Call |
| 114 | `base::test::ScopedTaskEnvironment::RunUntilIdle()` to wait until all tasks have |
| 115 | run. |
| 116 | |
| 117 | In both cases, you can run tasks until a condition is met. A test that waits for |
| 118 | a condition to be met is easier to understand and debug than a test that waits |
| 119 | for all tasks to run. |
| 120 | |
| 121 | ```cpp |
| 122 | int g_condition = false; |
| 123 | |
| 124 | base::RunLoop run_loop; |
| 125 | base::PostTaskWithTraits(FROM_HERE, {}, base::BindOnce( |
| 126 | [] (base::OnceClosure closure) { |
| 127 | g_condition = true; |
| 128 | std::move(quit_closure).Run(); |
| 129 | }, run_loop.QuitClosure())); |
| 130 | |
| 131 | // Runs tasks until the quit closure is invoked. |
| 132 | run_loop.Run(); |
| 133 | |
| 134 | EXPECT_TRUE(g_condition); |
| 135 | ``` |
| 136 | |
| 137 | ## Your question hasn't been answered? |
| 138 | |
| 139 | Ping |
| 140 | [scheduler-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/scheduler-dev). |