0

Uncontroversial and easy to carve out bits of deprecated BrowserThreads cleanup.

Overall reland is at https://chromium-review.googlesource.com/c/chromium/src/+/705775,
these are the easy bits that can land first (and wrap up contributions to issues
365909 and 752144 as well as to the ios/ side of 689520).

NOPRESUBMIT=TRUE (for BrowserThread presubmit triggered by touching related code)
TBR=jam@chromium.org, jsbell@chromium.org, sdefresne@chromium.org, asvitkine@chromium.org

Bug: 689520, 365909, 752144
Change-Id: I9b2b5326a12de5f7ffa22f8ac3332c8e57a6e14f
Reviewed-on: https://chromium-review.googlesource.com/738469
Commit-Queue: Gabriel Charette <gab@chromium.org>
Reviewed-by: Sylvain Defresne <sdefresne@chromium.org>
Reviewed-by: Gabriel Charette <gab@chromium.org>
Cr-Commit-Position: refs/heads/master@{#511551}
This commit is contained in:
Gabriel Charette
2017-10-25 19:31:15 +00:00
committed by Commit Bot
parent 5996c2e9d3
commit e9748f27c3
21 changed files with 98 additions and 701 deletions

@ -59,8 +59,7 @@ static void FlushTaskRunner(base::SequencedTaskRunner* task_runner) {
class WebRtcRtpDumpWriterTest : public testing::Test {
public:
WebRtcRtpDumpWriterTest()
: thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP |
content::TestBrowserThreadBundle::REAL_FILE_THREAD),
: thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
temp_dir_(new base::ScopedTempDir()) {}
virtual void SetUp() {

@ -86,24 +86,27 @@ NOINLINE void ThreadUnresponsive_IO() {
ReportThreadHang();
}
NOINLINE void CrashBecauseThreadWasUnresponsive(int thread_id) {
// TODO(rtenneti): The following is a temporary change to check thread_id
// numbers explicitly so that we will have minimum code. Will change after the
// test run to use content::BrowserThread::ID enum.
if (thread_id == 0)
return ThreadUnresponsive_UI();
else if (thread_id == 1)
return ThreadUnresponsive_DB();
else if (thread_id == 2)
return ThreadUnresponsive_FILE();
else if (thread_id == 3)
return ThreadUnresponsive_FILE_USER_BLOCKING();
else if (thread_id == 4)
return ThreadUnresponsive_PROCESS_LAUNCHER();
else if (thread_id == 5)
return ThreadUnresponsive_CACHE();
else if (thread_id == 6)
return ThreadUnresponsive_IO();
NOINLINE void CrashBecauseThreadWasUnresponsive(
content::BrowserThread::ID thread_id) {
switch (thread_id) {
case content::BrowserThread::UI:
return ThreadUnresponsive_UI();
case content::BrowserThread::DB:
return ThreadUnresponsive_DB();
case content::BrowserThread::FILE:
return ThreadUnresponsive_FILE();
case content::BrowserThread::FILE_USER_BLOCKING:
return ThreadUnresponsive_FILE_USER_BLOCKING();
case content::BrowserThread::PROCESS_LAUNCHER:
return ThreadUnresponsive_PROCESS_LAUNCHER();
case content::BrowserThread::CACHE:
return ThreadUnresponsive_CACHE();
case content::BrowserThread::IO:
return ThreadUnresponsive_IO();
case content::BrowserThread::ID_COUNT:
NOTREACHED();
break;
}
}
} // namespace metrics

@ -6,6 +6,7 @@
#define CHROME_BROWSER_METRICS_THREAD_WATCHER_REPORT_HANG_H_
#include "base/compiler_specific.h"
#include "content/public/browser/browser_thread.h"
namespace metrics {
@ -19,7 +20,8 @@ NOINLINE void ShutdownHang();
// This function makes it possible to tell from the callstack alone what thread
// was unresponsive.
NOINLINE void CrashBecauseThreadWasUnresponsive(int thread_id);
NOINLINE void CrashBecauseThreadWasUnresponsive(
content::BrowserThread::ID thread_id);
} // namespace metrics

@ -1296,6 +1296,9 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() {
service_manager_context_.reset();
mojo_ipc_support_.reset();
if (save_file_manager_)
save_file_manager_->Shutdown();
{
base::ThreadRestrictions::ScopedAllowWait allow_wait_for_join;
@ -1326,10 +1329,6 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() {
}
case BrowserThread::FILE: {
TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:FileThread");
// Clean up state that lives on or uses the FILE thread before it goes
// away.
if (save_file_manager_)
save_file_manager_->Shutdown();
ResetThread_FILE();
break;
}

@ -32,39 +32,39 @@ class BrowserThreadTest : public testing::Test {
protected:
void SetUp() override {
ui_thread_.reset(new BrowserThreadImpl(BrowserThread::UI));
file_thread_.reset(new BrowserThreadImpl(BrowserThread::FILE));
io_thread_.reset(new BrowserThreadImpl(BrowserThread::IO));
ui_thread_->Start();
file_thread_->Start();
io_thread_->Start();
}
void TearDown() override {
StopUIThread();
file_thread_->Stop();
io_thread_->Stop();
ui_thread_ = nullptr;
file_thread_ = nullptr;
io_thread_ = nullptr;
BrowserThreadImpl::ResetGlobalsForTesting(BrowserThread::UI);
BrowserThreadImpl::ResetGlobalsForTesting(BrowserThread::FILE);
BrowserThreadImpl::ResetGlobalsForTesting(BrowserThread::IO);
}
static void BasicFunction(base::MessageLoop* message_loop) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
message_loop->task_runner()->PostTask(
FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
}
class DeletedOnFile
: public base::RefCountedThreadSafe<
DeletedOnFile, BrowserThread::DeleteOnFileThread> {
class DeletedOnIO
: public base::RefCountedThreadSafe<DeletedOnIO,
BrowserThread::DeleteOnIOThread> {
public:
explicit DeletedOnFile(base::MessageLoop* message_loop)
explicit DeletedOnIO(base::MessageLoop* message_loop)
: message_loop_(message_loop) {}
private:
friend struct BrowserThread::DeleteOnThread<BrowserThread::FILE>;
friend class base::DeleteHelper<DeletedOnFile>;
friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
friend class base::DeleteHelper<DeletedOnIO>;
~DeletedOnFile() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
~DeletedOnIO() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
message_loop_->task_runner()->PostTask(
FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
}
@ -74,7 +74,7 @@ class BrowserThreadTest : public testing::Test {
private:
std::unique_ptr<BrowserThreadImpl> ui_thread_;
std::unique_ptr<BrowserThreadImpl> file_thread_;
std::unique_ptr<BrowserThreadImpl> io_thread_;
// It's kind of ugly to make this mutable - solely so we can post the Quit
// Task from Release(). This should be fixed.
mutable base::MessageLoop loop_;
@ -120,7 +120,7 @@ class UIThreadDestructionObserver
TEST_F(BrowserThreadTest, PostTask) {
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
BrowserThread::IO, FROM_HERE,
base::BindOnce(&BasicFunction, base::MessageLoop::current()));
base::RunLoop().Run();
}
@ -132,15 +132,15 @@ TEST_F(BrowserThreadTest, Release) {
TEST_F(BrowserThreadTest, ReleasedOnCorrectThread) {
{
scoped_refptr<DeletedOnFile> test(
new DeletedOnFile(base::MessageLoop::current()));
scoped_refptr<DeletedOnIO> test(
new DeletedOnIO(base::MessageLoop::current()));
}
base::RunLoop().Run();
}
TEST_F(BrowserThreadTest, PostTaskViaTaskRunner) {
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE);
BrowserThread::GetTaskRunnerForThread(BrowserThread::IO);
task_runner->PostTask(
FROM_HERE, base::BindOnce(&BasicFunction, base::MessageLoop::current()));
base::RunLoop().Run();
@ -157,7 +157,7 @@ TEST_F(BrowserThreadTest, PostTaskAndReply) {
// Most of the heavy testing for PostTaskAndReply() is done inside the
// task runner test. This just makes sure we get piped through at all.
ASSERT_TRUE(BrowserThread::PostTaskAndReply(
BrowserThread::FILE, FROM_HERE, base::BindOnce(&base::DoNothing),
BrowserThread::IO, FROM_HERE, base::BindOnce(&base::DoNothing),
base::BindOnce(&base::RunLoop::QuitCurrentWhenIdleDeprecated)));
base::RunLoop().Run();
}

@ -187,7 +187,6 @@ class MultiThreadFileSystemOperationRunnerTest : public testing::Test {
public:
MultiThreadFileSystemOperationRunnerTest()
: thread_bundle_(
content::TestBrowserThreadBundle::REAL_FILE_THREAD |
content::TestBrowserThreadBundle::IO_MAINLOOP) {}
void SetUp() override {

@ -8,8 +8,11 @@
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/task_scheduler/post_task.h"
#include "base/threading/platform_thread.h"
#include "base/values.h"
#include "content/browser/indexed_db/indexed_db_context_impl.h"
@ -225,8 +228,7 @@ void IndexedDBInternalsUI::DownloadOriginDataOnIndexedDBThread(
if (!temp_dir.CreateUniqueTempDir())
return;
// This will get cleaned up on the File thread after the download
// has completed.
// This will get cleaned up after the download has completed.
base::FilePath temp_path = temp_dir.Take();
std::string origin_id = storage::GetIdentifierFromOrigin(origin.GetURL());
@ -346,7 +348,7 @@ void FileDeleter::OnDownloadUpdated(DownloadItem* item) {
case DownloadItem::CANCELLED:
case DownloadItem::INTERRUPTED: {
item->RemoveObserver(this);
BrowserThread::DeleteOnFileThread::Destruct(this);
delete this;
break;
}
default:
@ -355,9 +357,11 @@ void FileDeleter::OnDownloadUpdated(DownloadItem* item) {
}
FileDeleter::~FileDeleter() {
base::ScopedTempDir path;
bool will_delete = path.Set(temp_dir_);
DCHECK(will_delete);
base::PostTaskWithTraits(FROM_HERE,
{base::MayBlock(), base::TaskPriority::BACKGROUND,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
base::Bind(base::IgnoreResult(&base::DeleteFile),
std::move(temp_dir_), true));
}
void IndexedDBInternalsUI::OnDownloadStarted(

@ -236,12 +236,14 @@ class CONTENT_EXPORT BrowserThread {
// Use these templates in conjunction with RefCountedThreadSafe or scoped_ptr
// when you want to ensure that an object is deleted on a specific thread.
// This is needed when an object can hop between threads
// (i.e. IO -> FILE -> IO), and thread switching delays can mean that the
// final IO tasks executes before the FILE task's stack unwinds.
// This would lead to the object destructing on the FILE thread, which often
// is not what you want (i.e. to unregister from NotificationService, to
// notify other objects on the creating thread etc).
// This is needed when an object can hop between threads (i.e. UI -> IO ->
// UI), and thread switching delays can mean that the final UI tasks executes
// before the IO task's stack unwinds. This would lead to the object
// destructing on the IO thread, which often is not what you want (i.e. to
// unregister from NotificationService, to notify other objects on the
// creating thread etc). Note: see base::OnTaskRunnerDeleter and
// base::RefCountedDeleteOnSequence to bind to SequencedTaskRunner instead of
// specific BrowserThreads.
template<ID thread>
struct DeleteOnThread {
template<typename T>
@ -280,13 +282,10 @@ class CONTENT_EXPORT BrowserThread {
// Sample usage with scoped_ptr:
// std::unique_ptr<Foo, BrowserThread::DeleteOnIOThread> ptr;
//
// Note: when migrating BrowserThreads to TaskScheduler based
// SequencedTaskRunners these map to base::OnTaskRunnerDeleter and
// base::RefCountedDeleteOnSequence.
// Note: see base::OnTaskRunnerDeleter and base::RefCountedDeleteOnSequence to
// bind to SequencedTaskRunner instead of specific BrowserThreads.
struct DeleteOnUIThread : public DeleteOnThread<UI> { };
struct DeleteOnIOThread : public DeleteOnThread<IO> { };
struct DeleteOnFileThread : public DeleteOnThread<FILE> { };
struct DeleteOnDBThread : public DeleteOnThread<DB> { };
// Returns an appropriate error message for when DCHECK_CURRENTLY_ON() fails.
static std::string GetDCheckCurrentlyOnErrorMessage(ID expected);

@ -124,22 +124,10 @@ void TestBrowserThreadBundle::Init() {
void TestBrowserThreadBundle::CreateBrowserThreads() {
CHECK(!threads_created_);
if (options_ & REAL_DB_THREAD) {
db_thread_ = base::MakeUnique<TestBrowserThread>(BrowserThread::DB);
db_thread_->Start();
} else {
db_thread_ = base::MakeUnique<TestBrowserThread>(
BrowserThread::DB, base::MessageLoop::current());
}
if (options_ & REAL_FILE_THREAD) {
file_thread_ = base::MakeUnique<TestBrowserThread>(BrowserThread::FILE);
file_thread_->Start();
} else {
file_thread_ = base::MakeUnique<TestBrowserThread>(
BrowserThread::FILE, base::MessageLoop::current());
}
db_thread_ = base::MakeUnique<TestBrowserThread>(
BrowserThread::DB, base::MessageLoop::current());
file_thread_ = base::MakeUnique<TestBrowserThread>(
BrowserThread::FILE, base::MessageLoop::current());
file_user_blocking_thread_ = base::MakeUnique<TestBrowserThread>(
BrowserThread::FILE_USER_BLOCKING, base::MessageLoop::current());
process_launcher_thread_ = base::MakeUnique<TestBrowserThread>(

@ -112,10 +112,8 @@ class TestBrowserThreadBundle {
enum Options {
DEFAULT = 0,
IO_MAINLOOP = 1 << 0,
REAL_DB_THREAD = 1 << 1,
REAL_FILE_THREAD = 1 << 2,
REAL_IO_THREAD = 1 << 3,
DONT_CREATE_BROWSER_THREADS = 1 << 4,
REAL_IO_THREAD = 1 << 1,
DONT_CREATE_BROWSER_THREADS = 1 << 2,
};
TestBrowserThreadBundle();

@ -1,511 +1,4 @@
# Threading
[TOC]
## 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.
The `Thread` object is defined in
[`base/threading/thread.h`](https://cs.chromium.org/chromium/src/base/threading/thread.h).
In general you should probably use one of the existing threads described below
rather than make new ones. We already have a lot of threads that are difficult
to keep track of. Each thread has a `MessageLoop` (see
[`base/message_loop/message_loop.h`](https://cs.chromium.org/chromium/src/base/message_loop/message_loop.h)
that processes messages for that thread. You can get the message loop for a
thread using the `Thread.message_loop()` function. More details about
`MessageLoop` can be found in
[Anatomy of Chromium MessageLoop](https://docs.google.com/document/d/1_pJUHO3f3VyRSQjEhKVvUU7NzCyuTCQshZvbWeQiCXU/view#).
## Existing threads
Most threads are managed by the BrowserProcess object, which acts as the service
manager for the main "browser" process. By default, everything happens on the UI
thread. We have pushed certain classes of processing into these other
threads. It has getters for the following threads:
* **ui_thread**: Main thread where the application starts up.
* **io_thread**: This thread is somewhat mis-named. It is the dispatcher thread
that handles communication between the browser process and all the
sub-processes. It is also where all resource requests (web page loads) are
dispatched from (see
[Multi-process Architecture](https://www.chromium.org/developers/design-documents/multi-process-architecture)).
* **file_thread**: A general process thread for file operations. When you want to
do blocking filesystem operations (for example, requesting an icon for a file
type, or writing downloaded files to disk), dispatch to this thread.
* **db_thread**: A thread for database operations. For example, the cookie
service does sqlite operations on this thread. Note that the history database
doesn't use this thread yet.
* **safe_browsing_thread**
Several components have their own threads:
* **History**: The history service object has its own thread. This might be
merged with the db_thread above. However, we need to be sure that things
happen in the correct order -- for example, that cookies are loaded before
history since cookies are needed for the first load, and history
initialization is long and will block it.
* **Proxy service**: See
[`net/http/http_proxy_service.cc`](https://cs.chromium.org/chromium/src/net/http/http_proxy_service.cc).
* **Automation proxy**: This thread is used to communicate with the UI test
program driving the app.
## Keeping the browser responsive
As hinted in the overview, we avoid doing any blocking I/O on the UI thread to
keep the UI responsive. Less apparent is that we also need to avoid blocking
I/O on the IO thread. The reason is that if we block it for an expensive
operation, say disk access, then IPC messages don't get processed. The effect
is that the user can't interact with a page. Note that asynchronous/overlapped
I/O are fine.
Another thing to watch out for is to not block threads on one another. 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 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 is covered in the
PostTask section below.
## Getting stuff to other threads
### `base::Callback<>`, Async APIs and Currying
A `base::Callback<>` (see the docs in
[`base/callback.h`](https://cs.chromium.org/chromium/src/base/callback.h) is
a templated class with a `Run()` method. It is a generalization of a function
pointer and is created by a call to `base::Bind`. Async APIs often will take a
`base::Callback<>` as a means to asynchronously return the results of an
operation. Here is an example of a hypothetical FileRead API.
void ReadToString(const std::string& filename, const base::Callback<void(const std::string&)>& on_read);
void DisplayString(const std::string& result) {
LOG(INFO) << result;
}
void SomeFunc(const std::string& file) {
ReadToString(file, base::Bind(&DisplayString));
};
In the example above, `base::Bind` takes the function pointer `&DisplayString`
and turns it into a `base::Callback<void(const std::string& result)>`. The type
of the generated `base::Callback<>` is inferred from the arguments. Why not
just pass the function pointer directly? The reason is `base::Bind` allows the
caller to adapt function interfaces and/or attach extra context
via [Currying](http://en.wikipedia.org/wiki/Currying). For instance, if we had
a utility function `DisplayStringWithPrefix` that took an extra argument with
the prefix, we use `base::Bind` to adapt the interface as follows.
void DisplayStringWithPrefix(const std::string& prefix, const std::string& result) {
LOG(INFO) << prefix << result;
}
void AnotherFunc(const std::string& file) {
ReadToString(file, base::Bind(&DisplayStringWithPrefix, "MyPrefix: "));
};
This can be used in lieu of creating an adapter functions a small classes that
holds prefix as a member variable. Notice also that the `"MyPrefix: "` argument
is actually a `const char*`, while `DisplayStringWithPrefix` actually wants a
`const std::string&`. Like normal function dispatch, `base::Bind`, will coerce
parameters types if possible.
See [How arguments are handled by base::Bind()](#how_arguments_are_handled)
below for more details about argument storage, copying, and special handling of
references.
### PostTask
The lowest level of dispatching to another thread is to use the
`MessageLoop.PostTask` and `MessageLoop.PostDelayedTask`
(see
[`base/message_loop/message_loop.h`](https://cs.chromium.org/chromium/src/base/message_loop/message_loop.h)).
PostTask schedules a task to be run on a particular thread. A task is defined
as a `base::Closure`, which is a typedef for a
`base::Callback<void(void)>`. `PostDelayedTask` schedules a task to be run after
a delay on a particular thread. A task is represented by the `base::Closure`
typedef, which contains a `Run()` function, and is created by calling
`base::Bind()`. To process a task, the message loop eventually calls
`base::Closure`'s `Run` function, and then drops the reference to the task
object. Both `PostTask` and `PostDelayedTask` take a `base::Location`
parameter, which is used for lightweight debugging purposes (counts and
primitive profiling of pending and completed tasks can be monitored in a debug
build via the url about:objects). Generally the macro value `FROM_HERE` is the
appropriate value to use in this parameter.
Note that new tasks go on the message loop's queue, and any delay that is
specified is subject to the operating system's timer resolutions. This means
that under Windows, very small timeouts (under 10ms) will likely not be honored
(and will be longer). Using a timeout of 0 in `PostDelayedTask` is equivalent to
calling `PostTask`, and adds no delay beyond queuing delay. `PostTask` is also
used to do something on the current thread "sometime after the current
processing returns to the message loop." Such a continuation on the current
thread can be used to assure that other time critical tasks are not starved on
this thread.
The following is an example of a creating a task for a function and posting it
to another thread (in this example, the file thread):
void WriteToFile(const std::string& filename, const std::string& data);
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&WriteToFile, "foo.txt", "hello world!"));
You should always use `BrowserThread` to post tasks between threads. Never
cache `MessageLoop` pointers as it can cause bugs such as the pointers being
deleted while you're still holding on to them. More information can be
found
[here](https://www.chromium.org/developers/design-documents/threading/suble-threading-bugs-and-patterns-to-avoid-them).
### base::Bind() and class methods.
The `base::Bind()` API also supports invoking class methods as well. The syntax
is very similar to calling `base::Bind()` on a function, except the first
argument should be the object the method belongs to. By default, the object that
`PostTask` uses must be a thread-safe reference-counted object. Reference
counting ensures that the object invoked on another thread will stay alive until
the task completes.
class MyObject : public base::RefCountedThreadSafe<MyObject> {
public:
void DoSomething(const std::string16& name) {
thread_->message_loop()->PostTask(
FROM_HERE, base::Bind(&MyObject::DoSomethingOnAnotherThread, this, name));
}
void DoSomethingOnAnotherThread(const std::string16& name) {
...
}
private:
// Always good form to make the destructor private so that only RefCounted
// ThreadSafe can access it.
// This avoids bugs with double deletes.
friend class base::RefCountedThreadSafe<MyObject>;
~MyObject();
Thread* thread_;
};
If you have external synchronization structures that can completely ensure that
an object will always be alive while the task is waiting to execute, you can
wrap the object pointer with `base::Unretained()` when calling `base::Bind()` to
disable the refcounting. This will also allow using `base::Bind()` on classes
that are not refcounted. Be careful when doing this!
### How arguments are handled by `base::Bind()`
<a id="how_arguments_are_handled"></a>
The arguments given to `base::Bind()` are copied into an internal
`InvokerStorage` structure object (defined in
[`base/bind_internal.h`](http://cs.chromium.org/chromium/src/base/bind_internal.h).
When the function is finally executed, it will see copies of the arguments. This is important if your target function or method takes a const reference; the
reference will be to a copy of the argument. If you need a reference to the
original argument, you can wrap the argument with `base::ConstRef()`. Use this
carefully as it is likely dangerous if target of the reference cannot be
guaranteed to live past when the task is executed. In particular, it is almost
never safe to use `base::ConstRef()` to a variable on the stack unless you can
guarantee the stack frame will not be invalidated until the asynchronous task
finishes.
Sometimes, you will want to pass reference-counted objects as parameters (be
sure to use `RefCountedThreadSafe` and not plain `RefCounted` as the base class
for these objects). To ensure that the object lives throughout the entire
request, the Closure generated by `base::Bind` must keep a reference to it. This
can be done by passing scoped_refptr as the parameter type, or by wrapping the
raw pointer with `base::WrapRefCounted()`:
class SomeParamObject : public base::RefCountedThreadSafe<SomeParamObject> {
...
};
class MyObject : public base::RefCountedThreadSafe<MyObject> {
public:
void DoSomething() {
scoped_refptr<SomeParamObject> param(new SomeParamObject);
// Without RetainedRef, the scoped_refptr would try to implicitly
// convert to a raw pointer and fail compilation.
thread_->message_loop()->PostTask(FROM_HERE,
base::Bind(&MyObject::DoSomethingOnAnotherThread, this,
base::RetainedRef(param)));
}
void DoSomething2() {
SomeParamObject* param = new SomeParamObject;
thread_->message_loop()->PostTask(FROM_HERE,
base::Bind(&MyObject::DoSomethingOnAnotherThread, this,
base::RetainedRef(base::WrapRefCounted(param))));
}
void DoSomething3() {
scoped_refptr<SomeParamObject> param(new SomeParamObject);
// Moving |param| prevents an extra AddRef()/Release() pair.
thread_->message_loop()->PostTask(FROM_HERE,
base::Bind(&MyObject::DoSomethingOnAnotherThread, this,
base::RetainedRef(std::move(param))));
}
// Note how this takes a raw pointer. The important part is that
// base::Bind() was passed a scoped_refptr; using a scoped_refptr
// here would result in an extra AddRef()/Release() pair.
void DoSomethingOnAnotherThread(SomeParamObject* param) {
...
}
};
If you want to pass the object without taking a reference on it, wrap the
argument with `base::Unretained()`. Again, using this means there are external
guarantees on the lifetime of the object, so tread carefully!
If your object has a non-trivial destructor that needs to run on a specific
thread, you can use the following trait. This is needed since timing races could
lead to a task completing execution before the code that posted it has unwound
the stack.
class MyObject : public base::RefCountedThreadSafe<MyObject, BrowserThread::DeleteOnIOThread> {
## Callback cancellation
There are 2 major reasons to cancel a task (in the form of a Callback):
* You want to do something later on your object, but at the time your callback
runs, your object may have been destroyed.
* When input changes (e.g. user input), old tasks become unnecessary. For
performance considerations, you should cancel them.
See following about different approaches for cancellation.
### Important notes about cancellation
It's dangerous to cancel a task with owned parameters. See the following
example. (The example uses `base::WeakPtr` for cancellation, but the problem
applies to all approaches).
class MyClass {
public:
// Owns |p|.
void DoSomething(AnotherClass* p) {
...
}
WeakPtr<MyClass> AsWeakPtr() {
return weak_factory_.GetWeakPtr();
}
private:
base::WeakPtrFactory<MyClass> weak_factory_;
};
...
Closure cancelable_closure = Bind(&MyClass::DoSomething, object->AsWeakPtr(), p);
Callback<void(AnotherClass*)> cancelable_callback = Bind(&MyClass::DoSomething, object->AsWeakPtr());
...
void FunctionRunLater(const Closure& cancelable_closure,
const Callback<void(AnotherClass*)>& cancelable_callback) {
...
// Leak memory!
cancelable_closure.Run();
cancelable_callback.Run(p);
}
In `FunctionRunLater`, both `Run()` calls will leak `p` when object is already
destructed. Using `scoped_ptr` can fix the bug:
class MyClass {
public:
void DoSomething(scoped_ptr<AnotherClass> p) {
...
}
...
};
### base::WeakPtr and Cancellation __[NOT THREAD SAFE]__
You can use a `base::WeakPtr` and `base::WeakPtrFactory`
(in
[base/memory/weak_ptr.h](https://cs.chromium.org/chromium/src/base/memory/weak_ptr.h))
to ensure that any invokes can not outlive the object they are being invoked on,
without using reference counting. The `base::Bind` mechanism has special
understanding for `base::WeakPtr` that will disable the task's execution if the
`base::WeakPtr` has been invalidated. The `base::WeakPtrFactory` object can be
used to generate `base::WeakPtr` instances that know about the factory
object. When the factory is destroyed, all the `base::WeakPtr` will have their
internal "invalidated" flag set, which will make any tasks bound to them to not
dispatch. By putting the factory as a member of the object being dispatched to,
you can get automatic cancellation.
__NOTE__: This only works when the task is posted to the same thread. Currently
there is not a general solution that works for tasks posted to other
threads. See
the [next section about CancelableTaskTracker](#cancelable_task_tracker) for an
alternative solution.
class MyObject {
public:
MyObject() : weak_factory_(this) {}
void DoSomething() {
const int kDelayMS = 100;
MessageLoop::current()->PostDelayedTask(FROM_HERE,
base::Bind(&MyObject::DoSomethingLater, weak_factory_.GetWeakPtr()),
kDelayMS);
}
void DoSomethingLater() {
...
}
private:
base::WeakPtrFactory<MyObject> weak_factory_;
};
### CancelableTaskTracker
<a id="cancelable_task_tracker"></a>
While `base::WeakPtr` is very helpful to cancel a task, it is not thread safe so
can not be used to cancel tasks running on another thread. This is sometimes a
performance critical requirement. E.g. We need to cancel database lookup task on
DB thread when user changes inputed text. In this kind of situation
`CancelableTaskTracker` is appropriate.
With `CancelableTaskTracker` you can cancel a single task with returned
`TaskId`. This is another reason to use `CancelableTaskTracker` instead of
`base::WeakPtr`, even in a single thread context.
`CancelableTaskTracker` has 2 `Post` methods doing the same thing as the ones in
`base::TaskRunner`, with additional cancellation support.
class UserInputHandler : public base::RefCountedThreadSafe<UserInputHandler> {
// Runs on UI thread.
void OnUserInput(Input input) {
CancelPreviousTask();
DBResult* result = new DBResult();
task_id_ = tracker_->PostTaskAndReply(
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB).get(),
FROM_HERE,
base::Bind(&LookupHistoryOnDBThread, this, input, result),
base::Bind(&ShowHistoryOnUIThread, this, base::Owned(result)));
}
void CancelPreviousTask() {
tracker_->TryCancel(task_id_);
}
...
private:
CancelableTaskTracker tracker_; // Cancels all pending tasks while destruction.
CancelableTaskTracker::TaskId task_id_;
...
};
Since task runs on other threads, there's no guarantee it can be successfully
canceled.
When `TryCancel()` is called:
* If neither task nor reply has started running, both will be canceled.
* If task is already running or has finished running, reply will be canceled.
* If reply is running or has finished running, cancelation is a noop.
Like `base::WeakPtrFactory`, `CancelableTaskTracker` will cancel all tasks on
destruction.
### Cancelable request __(DEPRECATED)__
Note. Cancelable request is deprecated. Please do not use it in new code. For
canceling tasks running on the same thread, use WeakPtr. For canceling tasks
running on a different thread, use `CancelableTaskTracker`.
A cancelable request makes it easier to make requests to another thread with
that thread returning some data to you asynchronously. Like the revokable store
system, it uses objects that track whether the originating object is alive. When
the calling object is deleted, the request will be canceled to prevent invalid
callbacks.
Like the revokable store system, a user of a cancelable request has
an object (here, called a _Consumer_) that tracks whether it is alive and will
auto-cancel any outstanding requests on deleting.
class MyClass {
void MakeRequest() {
frontend_service->StartRequest(some_input1, some_input2, this,
// Use base::Unretained(this) if this may cause a refcount cycle.
base::Bind(&MyClass:RequestComplete, this));
}
void RequestComplete(int status) {
...
}
private:
CancelableRequestConsumer consumer_;
};
Note that the `MyClass::RequestComplete`, is bounded with
`base::Unretained(this)` here.
The consumer also allows you to associate extra data with a request. Use
`CancelableRequestConsumer` which will allow you to associate arbitrary data
with the handle returned by the provider service when you invoke the
request. The data will be automatically destroyed when the request is canceled.
A service handling requests inherits from `CancelableRequestProvider`. This
object provides methods for canceling in-flight requests, and will work with the
consumers to make sure everything is cleaned up properly on cancel. This
frontend service just tracks the request and sends it to a backend service on
another thread for actual processing. It would look like this:
class FrontendService : public CancelableRequestProvider {
typedef base::Callback<void(int)> RequestCallbackType;
Handle StartRequest(int some_input1, int some_input2,
CallbackConsumer* consumer,
const RequestCallbackType& callback) {
scoped_refptr<CancelableRequest<FrontendService::RequestCallbackType>>
request(new CancelableRequest(callback));
AddRequest(request, consumer);
// Send the parameters and the request to the backend thread.
backend_thread_->PostTask(FROM_HERE,
base::Bind(&BackendService::DoRequest, backend_, request,
some_input1, some_input2), 0);
// The handle will have been set by AddRequest.
return request->handle();
}
};
The backend service runs on another thread. It does processing and forwards the
result back to the original caller. It would look like this:
class BackendService : public base::RefCountedThreadSafe<BackendService> {
void DoRequest(
scoped_refptr<CancelableRequest<FrontendService::RequestCallbackType>>
request,
int some_input1, int some_input2) {
if (request->canceled())
return;
... do your processing ...
// Execute ForwardResult() like you would do Run() on the base::Callback<>.
request->ForwardResult(return_value);
}
};
Superseded by new [`Threading and Tasks in Chrome`](../threading_and_tasks.md)
and [`Callback<> and Bind()`](../callback.md) documentation.

@ -86,8 +86,7 @@ const int kDeleteButtonItem = 0;
class PasswordDetailsCollectionViewControllerTest
: public CollectionViewControllerTest {
protected:
PasswordDetailsCollectionViewControllerTest()
: thread_bundle_(web::TestWebThreadBundle::REAL_DB_THREAD) {
PasswordDetailsCollectionViewControllerTest() {
origin_ = kSite;
form_.username_value = base::SysNSStringToUTF16(kUsername);
form_.password_value = base::SysNSStringToUTF16(kPassword);

@ -34,8 +34,7 @@ namespace {
class SavePasswordsCollectionViewControllerTest
: public CollectionViewControllerTest {
protected:
SavePasswordsCollectionViewControllerTest()
: thread_bundle_(web::TestWebThreadBundle::REAL_DB_THREAD) {}
SavePasswordsCollectionViewControllerTest() = default;
void SetUp() override {
TestChromeBrowserState::Builder test_cbs_builder;

@ -85,7 +85,6 @@ class WebMainLoop {
std::unique_ptr<WebThreadImpl> main_thread_;
// Members initialized in |RunMainMessageLoopParts()| ------------------------
std::unique_ptr<WebThreadImpl> db_thread_;
std::unique_ptr<WebThreadImpl> io_thread_;
// Members initialized in |WebThreadsStarted()| --------------------------

@ -133,44 +133,13 @@ int WebMainLoop::CreateThreads(
base::Thread::Options io_message_loop_options;
io_message_loop_options.message_loop_type = base::MessageLoop::TYPE_IO;
io_thread_ = std::make_unique<WebThreadImpl>(WebThread::IO);
io_thread_->StartWithOptions(io_message_loop_options);
// Start threads in the order they occur in the WebThread::ID
// enumeration, except for WebThread::UI which is the main
// thread.
//
// Must be size_t so we can increment it.
for (size_t thread_id = WebThread::UI + 1; thread_id < WebThread::ID_COUNT;
++thread_id) {
std::unique_ptr<WebThreadImpl>* thread_to_start = nullptr;
base::Thread::Options options;
// Only start IO thread above as this is the only WebThread besides UI (which
// is the main thread).
static_assert(WebThread::ID_COUNT == 2, "Unhandled WebThread");
switch (thread_id) {
// TODO(rohitrao): We probably do not need all of these threads. Remove
// the ones that serve no purpose. http://crbug.com/365909
case WebThread::DB:
thread_to_start = &db_thread_;
options.timer_slack = base::TIMER_SLACK_MAXIMUM;
break;
case WebThread::IO:
thread_to_start = &io_thread_;
options = io_message_loop_options;
break;
case WebThread::UI:
case WebThread::ID_COUNT:
default:
NOTREACHED();
break;
}
WebThread::ID id = static_cast<WebThread::ID>(thread_id);
if (thread_to_start) {
(*thread_to_start).reset(new WebThreadImpl(id));
(*thread_to_start)->StartWithOptions(options);
} else {
NOTREACHED();
}
}
created_threads_ = true;
return result_code_;
}
@ -207,34 +176,11 @@ void WebMainLoop::ShutdownThreadsAndCleanUp() {
service_manager_context_.reset();
// Must be size_t so we can subtract from it.
for (size_t thread_id = WebThread::ID_COUNT - 1;
thread_id >= (WebThread::UI + 1); --thread_id) {
// Find the thread object we want to stop. Looping over all valid
// WebThread IDs and DCHECKing on a missing case in the switch
// statement helps avoid a mismatch between this code and the
// WebThread::ID enumeration.
//
// The destruction order is the reverse order of occurrence in the
// WebThread::ID list. The rationale for the order is as
// follows (need to be filled in a bit):
//
//
// - (Not sure why DB stops last.)
switch (thread_id) {
case WebThread::DB:
db_thread_.reset();
break;
case WebThread::IO:
io_thread_.reset();
break;
case WebThread::UI:
case WebThread::ID_COUNT:
default:
NOTREACHED();
break;
}
}
io_thread_.reset();
// Only stop IO thread above as this is the only WebThread besides UI (which
// is the main thread).
static_assert(WebThread::ID_COUNT == 2, "Unhandled WebThread");
// Shutdown TaskScheduler after the other threads. Other threads such as the
// I/O thread may need to schedule work like closing files or flushing data

@ -49,8 +49,7 @@ class TestWebThreadBundle {
enum Options {
DEFAULT = 0,
IO_MAINLOOP = 1 << 0,
REAL_DB_THREAD = 1 << 1,
REAL_IO_THREAD = 1 << 2,
REAL_IO_THREAD = 1 << 1,
};
TestWebThreadBundle();
@ -63,7 +62,6 @@ class TestWebThreadBundle {
std::unique_ptr<base::test::ScopedTaskEnvironment> scoped_task_environment_;
std::unique_ptr<TestWebThread> ui_thread_;
std::unique_ptr<TestWebThread> db_thread_;
std::unique_ptr<TestWebThread> io_thread_;
DISALLOW_COPY_AND_ASSIGN(TestWebThreadBundle);

@ -65,9 +65,6 @@ class WebThread {
// The main thread in the browser.
UI,
// This is the thread that interacts with the database.
DB,
// This is the thread that processes non-blocking IO, i.e. IPC and network.
// Blocking IO should happen in TaskScheduler.
IO,
@ -205,7 +202,6 @@ class WebThread {
// std::unique_ptr<Foo, web::WebThread::DeleteOnIOThread> ptr;
struct DeleteOnUIThread : public DeleteOnThread<UI> {};
struct DeleteOnIOThread : public DeleteOnThread<IO> {};
struct DeleteOnDBThread : public DeleteOnThread<DB> {};
private:
friend class WebThreadImpl;

@ -36,8 +36,6 @@ TestWebThreadBundle::~TestWebThreadBundle() {
base::RunLoop().RunUntilIdle();
io_thread_.reset();
base::RunLoop().RunUntilIdle();
db_thread_.reset();
base::RunLoop().RunUntilIdle();
// This is the point at which the cache pool is normally shut down. So flush
// it again in case any shutdown tasks have been posted to the pool from the
// threads above.
@ -59,14 +57,6 @@ void TestWebThreadBundle::Init(int options) {
ui_thread_.reset(
new TestWebThread(WebThread::UI, base::MessageLoop::current()));
if (options & TestWebThreadBundle::REAL_DB_THREAD) {
db_thread_.reset(new TestWebThread(WebThread::DB));
db_thread_->Start();
} else {
db_thread_.reset(
new TestWebThread(WebThread::DB, base::MessageLoop::current()));
}
if (options & TestWebThreadBundle::REAL_IO_THREAD) {
io_thread_.reset(new TestWebThread(WebThread::IO));
io_thread_->StartIOThread();

@ -26,7 +26,6 @@ namespace {
// Friendly names for the well-known threads.
const char* const g_web_thread_names[WebThread::ID_COUNT] = {
"Web_UIThread", // UI
"Web_DBThread", // DB
"Web_IOThread", // IO
};
@ -149,12 +148,6 @@ NOINLINE void WebThreadImpl::UIThreadRun(base::RunLoop* run_loop) {
CHECK_GT(line_number, 0);
}
NOINLINE void WebThreadImpl::DBThreadRun(base::RunLoop* run_loop) {
volatile int line_number = __LINE__;
Thread::Run(run_loop);
CHECK_GT(line_number, 0);
}
NOINLINE void WebThreadImpl::IOThreadRun(base::RunLoop* run_loop) {
volatile int line_number = __LINE__;
Thread::Run(run_loop);
@ -169,8 +162,6 @@ void WebThreadImpl::Run(base::RunLoop* run_loop) {
switch (thread_id) {
case WebThread::UI:
return UIThreadRun(run_loop);
case WebThread::DB:
return DBThreadRun(run_loop);
case WebThread::IO:
return IOThreadRun(run_loop);
case WebThread::ID_COUNT:

@ -39,7 +39,6 @@ class WebThreadImpl : public WebThread, public base::Thread {
// The following are unique function names that makes it possible to tell
// the thread id from the callstack alone in crash dumps.
void UIThreadRun(base::RunLoop* run_loop);
void DBThreadRun(base::RunLoop* run_loop);
void IOThreadRun(base::RunLoop* run_loop);
static bool PostTaskHelper(WebThread::ID identifier,

@ -2,11 +2,11 @@
This tool is designed to test the usage of the SetThreadDescription WinAPI in
Chrome. In Chrome, the SetThreadDescription API has been enabled to set thread
names. However, since there is no tool support to retrieve thread names set by
GetThreadDescription, we'll still rely on SetNameInternal function in
GetThreadDescription, we'll still rely on SetNameInternal function in
platform_thread_win.cc to set thread names. Despite this, we need a tool to demo
the SetThreadDescription API works, even without the debugger to be present.
The problem setting can be referred to
The problem setting can be referred to
https://bugs.chromium.org/p/chromium/issues/detail?id=684203
This tool incorporates the GetThreadDescription API trying to get names of all
@ -20,11 +20,11 @@ version 1607, this tool can only be effective if running in this version or
later ones.
[How to use it]
Please download the three files (.cc, .sln, .vcxproj) and compile the code in
Visual Studio. Run "ShowThreadNames.exe" either from the build directory or from
Visual Studio. No parameters are needed. This tool allows interaction with the
user. Once launched, it will show "Please enter the process Id, or "quit" to
end the program :" on the terminal. Simply type in the ID of any Chrome process
Please download the three files (.cc, .sln, .vcxproj) and compile the code in
Visual Studio. Run "ShowThreadNames.exe" either from the build directory or from
Visual Studio. No parameters are needed. This tool allows interaction with the
user. Once launched, it will show "Please enter the process Id, or "quit" to
end the program :" on the terminal. Simply type in the ID of any Chrome process
you are interested in, and you will get output like below:
thread_ID thread_name
@ -37,10 +37,6 @@ thread_ID thread_name
2256 AudioThread
9308 BrokerEvent
5668 BrowserWatchdog
4352 Chrome_CacheThread
12268 Chrome_DBThread
8616 Chrome_FileThread
1072 Chrome_FileUserBlockingThread
8280 Chrome_HistoryThread
7472 Chrome_IOThread
6336 Chrome_ProcessLauncherThread
@ -55,6 +51,6 @@ thread_ID thread_name
8216 TaskSchedulerServiceThread
11088 VideoCaptureThread
The threads are sorted by their names. Note that some threads have no names in
this example. If checking them using Visual Studio debugger, it is found that
The threads are sorted by their names. Note that some threads have no names in
this example. If checking them using Visual Studio debugger, it is found that
they are ntdll.dll!WorkerThreads.