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:

committed by
Commit Bot

parent
5996c2e9d3
commit
e9748f27c3
chrome/browser
media
metrics
content
docs/design
ios
chrome
browser
web
tools/win/ShowThreadNames
@ -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.
|
||||
|
Reference in New Issue
Block a user