Commit 90480312 authored by Gabriel Charette's avatar Gabriel Charette Committed by Commit Bot

Bring useful paragraphs from old threading docs into new threading docs.

As requested @ https://chromium-review.googlesource.com/c/chromium/src/+/738469/8/docs/design/threading.md#b122

Already had "Keep the Browser Responsive" and "Use Sequences instead of Locks"
sections, but amended them with a bit more text from the old docs.

R=jam@chromium.org

Bug: 752144
Change-Id: I67857ad21f8cc5884aeb65fe08fe184f63deed44
Reviewed-on: https://chromium-review.googlesource.com/921041Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Commit-Queue: Gabriel Charette <gab@chromium.org>
Cr-Commit-Position: refs/heads/master@{#537307}
parent 992c22b4
...@@ -201,19 +201,33 @@ pointer. ...@@ -201,19 +201,33 @@ pointer.
base::Closure cb = base::Bind(&MyClass::MyFunc, this, 23, "hello world"); base::Closure cb = base::Bind(&MyClass::MyFunc, this, 23, "hello world");
``` ```
### Partial Binding Of Parameters ### Partial Binding Of Parameters (Currying)
You can specify some parameters when you create the callback, and specify the You can specify some parameters when you create the callback, and specify the
rest when you execute the callback. rest when you execute the callback.
When calling a function bound parameters are first, followed by unbound
parameters.
```cpp ```cpp
void MyFunc(int i, const std::string& str) {} void ReadIntFromFile(const std::string& filename,
base::Callback<void(const std::string&)> cb = base::Bind(&MyFunc, 23); base::OnceCallback<void(int)> on_read);
cb.Run("hello world");
void DisplayIntWithPrefix(const std::string& prefix, int result) {
LOG(INFO) << prefix << result;
}
void AnotherFunc(const std::string& file) {
ReadIntFromFile(file, base::BindOnce(&DisplayIntWithPrefix, "MyPrefix: "));
};
``` ```
When calling a function bound parameters are first, followed by unbound This technique is known as [Currying](http://en.wikipedia.org/wiki/Currying). It
parameters. should be used in lieu of creating an adapter class that holds the bound
arguments. Notice also that the `"MyPrefix: "` argument is actually a
`const char*`, while `DisplayIntWithPrefix` actually wants a
`const std::string&`. Like normal function dispatch, `base::Bind`, will coerce
parameter types if possible.
### Avoiding Copies with Callback Parameters ### Avoiding Copies with Callback Parameters
......
...@@ -4,6 +4,14 @@ ...@@ -4,6 +4,14 @@
## Overview ## Overview
Chromium is a very multithreaded product. We try to keep the UI as responsive as
possible, and this means not blocking the UI thread with any blocking I/O or
other expensive operations. Our approach is to use message passing as the way of
communicating between threads. We discourage locking and threadsafe
objects. Instead, objects live on only one thread, we pass messages between
threads for communication, and we use callback interfaces (implemented by
message passing) for most cross-thread requests.
### Threads ### Threads
Every Chrome process has Every Chrome process has
...@@ -55,7 +63,8 @@ that require mere thread-safety as it opens up scheduling paradigms that ...@@ -55,7 +63,8 @@ that require mere thread-safety as it opens up scheduling paradigms that
wouldn't be possible otherwise (sequences can hop threads instead of being stuck wouldn't be possible otherwise (sequences can hop threads instead of being stuck
behind unrelated work on a dedicated thread). Ability to hop threads also means behind unrelated work on a dedicated thread). Ability to hop threads also means
the thread count can dynamically adapt to the machine's true resource the thread count can dynamically adapt to the machine's true resource
availability (faster on bigger machines, avoids trashing on slower machines). availability (increased parallelism on bigger machines, avoids trashing
resources on smaller machines).
Many core APIs were recently made sequence-friendly (classes are rarely Many core APIs were recently made sequence-friendly (classes are rarely
thread-affine -- i.e. only when using thread-local-storage or third-party APIs thread-affine -- i.e. only when using thread-local-storage or third-party APIs
...@@ -140,7 +149,7 @@ ways of controlling tasks in tests). ...@@ -140,7 +149,7 @@ ways of controlling tasks in tests).
A sequence is a set of tasks that run one at a time in posting order (not A sequence is a set of tasks that run one at a time in posting order (not
necessarily on the same thread). To post tasks as part of a sequence, use a necessarily on the same thread). To post tasks as part of a sequence, use a
[`SequencedTaskRunner`](https://cs.chromium.org/chromium/src/base/sequenced_task_runner.h). [`SequencedTaskRunner`](https://cs.chromium.org/chromium/src/base/sequenced_task_runner.h).
### Posting to a New Sequence ### Posting to a New Sequence
...@@ -219,6 +228,23 @@ other_task_runner->PostTask(FROM_HERE, ...@@ -219,6 +228,23 @@ other_task_runner->PostTask(FROM_HERE,
base::BindOnce(&A::AddValue, base::Unretained(&a))); base::BindOnce(&A::AddValue, base::Unretained(&a)));
``` ```
Locks should only be used to swap in a shared data structure that can be
accessed on multiple threads. If one thread updates it based on expensive
computation or through disk access, then that slow work should be done without
holding on to the lock. Only when the result is available should the lock be
used to swap in the new data. An example of this is in PluginList::LoadPlugins
([`content/common/plugin_list.cc`](https://cs.chromium.org/chromium/src/content/
common/plugin_list.cc). If you must use locks,
[here](https://www.chromium.org/developers/lock-and-condition-variable) are some
best practices and pitfalls to avoid.
In order to write non-blocking code, many APIs in Chromium are asynchronous.
Usually this means that they either need to be executed on a particular
thread/sequence and will return results via a custom delegate interface, or they
take a `base::Callback<>` object that is called when the requested operation is
completed. Executing work on a specific thread/sequence is covered in the
PostTask sections above.
## Posting Multiple Tasks to the Same Thread ## Posting Multiple Tasks to the Same Thread
If multiple tasks need to run on the same thread, post them to a If multiple tasks need to run on the same thread, post them to a
...@@ -382,7 +408,8 @@ base::PostTaskWithTraits( ...@@ -382,7 +408,8 @@ base::PostTaskWithTraits(
Do not perform expensive work on the main thread, the IO thread or any sequence Do not perform expensive work on the main thread, the IO thread or any sequence
that is expected to run tasks with a low latency. Instead, perform expensive that is expected to run tasks with a low latency. Instead, perform expensive
work asynchronously using `base::PostTaskAndReply*()` or work asynchronously using `base::PostTaskAndReply*()` or
`SequencedTaskRunner::PostTaskAndReply()`. `SequencedTaskRunner::PostTaskAndReply()`. Note that asynchronous/overlapped
I/O on the IO thread are fine.
Example: Running the code below on the main thread will prevent the browser from Example: Running the code below on the main thread will prevent the browser from
responding to user input for a long time. responding to user input for a long time.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment