Index: content/browser/browser_main_loop.cc |
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc |
index f4b7eae0ba294a5cd96d7e39655eff0555045ddd..eb274bb20e1653abcd8559ecc1856546a2d6338b 100644 |
--- a/content/browser/browser_main_loop.cc |
+++ b/content/browser/browser_main_loop.cc |
@@ -30,7 +30,10 @@ |
#include "base/single_thread_task_runner.h" |
#include "base/strings/string_number_conversions.h" |
#include "base/strings/string_split.h" |
+#include "base/synchronization/waitable_event.h" |
#include "base/system_monitor/system_monitor.h" |
+#include "base/task_scheduler/post_task.h" |
+#include "base/task_scheduler/task_traits.h" |
#include "base/threading/thread_restrictions.h" |
#include "base/threading/thread_task_runner_handle.h" |
#include "base/timer/hi_res_timer_manager.h" |
@@ -298,38 +301,90 @@ void OnStoppedStartupTracing(const base::FilePath& trace_file) { |
MSVC_DISABLE_OPTIMIZE() |
MSVC_PUSH_DISABLE_WARNING(4748) |
-NOINLINE void ResetThread_DB(std::unique_ptr<BrowserProcessSubThread> thread) { |
+// Takes ownership of |task_runner| and blocks until it's empty. |
+void FlushThreadTaskRunner( |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
+ base::WaitableEvent thread_terminated( |
+ base::WaitableEvent::ResetPolicy::MANUAL, |
+ base::WaitableEvent::InitialState::NOT_SIGNALED); |
+ task_runner->PostTask(FROM_HERE, |
+ base::Bind(&base::WaitableEvent::Signal, |
+ base::Unretained(&thread_terminated))); |
+ thread_terminated.Wait(); |
+} |
+ |
+NOINLINE void ResetThread_DB( |
+ std::unique_ptr<BrowserProcessSubThread> thread, |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
+ DCHECK(!thread || !task_runner); |
volatile int inhibit_comdat = __LINE__; |
ALLOW_UNUSED_LOCAL(inhibit_comdat); |
- thread.reset(); |
+ if (thread) { |
+ thread.reset(); |
+ } else { |
+ BrowserThreadImpl::RedirectThreadIDToTaskRunner(BrowserThread::DB, nullptr); |
+ FlushThreadTaskRunner(std::move(task_runner)); |
gab
2016/11/08 20:40:44
fdoray@ just made me realize offline that although
|
+ } |
} |
NOINLINE void ResetThread_FILE( |
- std::unique_ptr<BrowserProcessSubThread> thread) { |
+ std::unique_ptr<BrowserProcessSubThread> thread, |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
+ DCHECK(!thread || !task_runner); |
volatile int inhibit_comdat = __LINE__; |
ALLOW_UNUSED_LOCAL(inhibit_comdat); |
- thread.reset(); |
+ if (thread) { |
+ thread.reset(); |
+ } else { |
+ BrowserThreadImpl::RedirectThreadIDToTaskRunner(BrowserThread::FILE, |
+ nullptr); |
+ FlushThreadTaskRunner(std::move(task_runner)); |
+ } |
} |
NOINLINE void ResetThread_FILE_USER_BLOCKING( |
- std::unique_ptr<BrowserProcessSubThread> thread) { |
+ std::unique_ptr<BrowserProcessSubThread> thread, |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
+ DCHECK(!thread || !task_runner); |
volatile int inhibit_comdat = __LINE__; |
ALLOW_UNUSED_LOCAL(inhibit_comdat); |
- thread.reset(); |
+ if (thread) { |
+ thread.reset(); |
+ } else { |
+ BrowserThreadImpl::RedirectThreadIDToTaskRunner( |
+ BrowserThread::FILE_USER_BLOCKING, nullptr); |
+ FlushThreadTaskRunner(std::move(task_runner)); |
+ } |
} |
NOINLINE void ResetThread_PROCESS_LAUNCHER( |
- std::unique_ptr<BrowserProcessSubThread> thread) { |
+ std::unique_ptr<BrowserProcessSubThread> thread, |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
+ DCHECK(!thread || !task_runner); |
volatile int inhibit_comdat = __LINE__; |
ALLOW_UNUSED_LOCAL(inhibit_comdat); |
- thread.reset(); |
+ if (thread) { |
+ thread.reset(); |
+ } else { |
+ BrowserThreadImpl::RedirectThreadIDToTaskRunner( |
+ BrowserThread::PROCESS_LAUNCHER, nullptr); |
+ FlushThreadTaskRunner(std::move(task_runner)); |
+ } |
} |
NOINLINE void ResetThread_CACHE( |
- std::unique_ptr<BrowserProcessSubThread> thread) { |
+ std::unique_ptr<BrowserProcessSubThread> thread, |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
+ DCHECK(!thread || !task_runner); |
volatile int inhibit_comdat = __LINE__; |
ALLOW_UNUSED_LOCAL(inhibit_comdat); |
- thread.reset(); |
+ if (thread) { |
+ thread.reset(); |
+ } else { |
+ BrowserThreadImpl::RedirectThreadIDToTaskRunner(BrowserThread::CACHE, |
+ nullptr); |
+ FlushThreadTaskRunner(std::move(task_runner)); |
+ } |
} |
NOINLINE void ResetThread_IO(std::unique_ptr<BrowserProcessSubThread> thread) { |
@@ -867,54 +922,120 @@ int BrowserMainLoop::CreateThreads() { |
for (size_t thread_id = BrowserThread::UI + 1; |
thread_id < BrowserThread::ID_COUNT; |
++thread_id) { |
- std::unique_ptr<BrowserProcessSubThread>* thread_to_start = NULL; |
+ // If this thread ID is backed by a real thread, |thread_to_start| will be |
+ // set to the appropriate |BrowserProcessSubThread*|. And |options| can be |
+ // updated away from its default. |
+ std::unique_ptr<BrowserProcessSubThread>* thread_to_start = nullptr; |
base::Thread::Options options; |
+ // Otherwise this thread ID will be backed by a SingleThreadTaskRunner in |
+ // which case |task_runner_to_create| will be set to the appropriate |
+ // |SingleThreadTaskRunner*| and |task_runner_traits| can be updated away |
+ // from its default. |execution_mode| defaults to |SINGLE_THREADED| as-is |
+ // appropriate to map most old browser threads but can also be updated in |
+ // the switch statement below if a thread has specific requirements. |
+ scoped_refptr<base::SingleThreadTaskRunner>* task_runner_to_create = |
+ nullptr; |
+ base::TaskTraits task_runner_traits; |
+ |
+ const bool redirect_nonUInonIO_browser_threads = |
+ GetContentClient() |
+ ->browser() |
+ ->RedirectNonUINonIOBrowserThreadsToTaskScheduler(); |
+ |
switch (thread_id) { |
case BrowserThread::DB: |
TRACE_EVENT_BEGIN1("startup", |
"BrowserMainLoop::CreateThreads:start", |
"Thread", "BrowserThread::DB"); |
- thread_to_start = &db_thread_; |
- options.timer_slack = base::TIMER_SLACK_MAXIMUM; |
+ if (redirect_nonUInonIO_browser_threads) { |
+ task_runner_to_create = &db_task_runner_; |
+ task_runner_traits.WithFileIO() |
+ .WithPriority(base::TaskPriority::USER_VISIBLE) |
+ .WithShutdownBehavior(base::TaskShutdownBehavior::BLOCK_SHUTDOWN); |
+ } else { |
+ thread_to_start = &db_thread_; |
+ options.timer_slack = base::TIMER_SLACK_MAXIMUM; |
+ } |
break; |
case BrowserThread::FILE_USER_BLOCKING: |
TRACE_EVENT_BEGIN1("startup", |
"BrowserMainLoop::CreateThreads:start", |
"Thread", "BrowserThread::FILE_USER_BLOCKING"); |
- thread_to_start = &file_user_blocking_thread_; |
+ if (redirect_nonUInonIO_browser_threads) { |
+ task_runner_to_create = &file_user_blocking_task_runner_; |
+ task_runner_traits.WithFileIO() |
+ .WithPriority(base::TaskPriority::USER_BLOCKING) |
+ .WithShutdownBehavior(base::TaskShutdownBehavior::BLOCK_SHUTDOWN); |
+ } else { |
+ thread_to_start = &file_user_blocking_thread_; |
+ } |
break; |
case BrowserThread::FILE: |
TRACE_EVENT_BEGIN1("startup", |
"BrowserMainLoop::CreateThreads:start", |
"Thread", "BrowserThread::FILE"); |
- thread_to_start = &file_thread_; |
+ |
#if defined(OS_WIN) |
- // On Windows, the FILE thread needs to be have a UI message loop |
- // which pumps messages in such a way that Google Update can |
- // communicate back to us. |
+ // On Windows, the FILE thread needs to be have a UI message loop which |
+ // pumps messages in such a way that Google Update can communicate back |
+ // to us. |
+ // TODO(robliao): Need to support COM in TaskScheduler before |
+ // redirecting the FILE thread on Windows. http://crbug.com/662122 |
+ // TODO(gab): Also need to support clipboard (see below). |
+ thread_to_start = &file_thread_; |
options = ui_message_loop_options; |
#else |
- options = io_message_loop_options; |
+ if (redirect_nonUInonIO_browser_threads) { |
+ task_runner_to_create = &file_task_runner_; |
+ task_runner_traits.WithFileIO() |
+ .WithPriority(base::TaskPriority::BACKGROUND) |
+ .WithShutdownBehavior(base::TaskShutdownBehavior::BLOCK_SHUTDOWN); |
+ } else { |
+ thread_to_start = &file_thread_; |
+ options = io_message_loop_options; |
+ options.timer_slack = base::TIMER_SLACK_MAXIMUM; |
+ } |
#endif |
- options.timer_slack = base::TIMER_SLACK_MAXIMUM; |
break; |
case BrowserThread::PROCESS_LAUNCHER: |
TRACE_EVENT_BEGIN1("startup", |
"BrowserMainLoop::CreateThreads:start", |
"Thread", "BrowserThread::PROCESS_LAUNCHER"); |
- thread_to_start = &process_launcher_thread_; |
- options.timer_slack = base::TIMER_SLACK_MAXIMUM; |
+ if (redirect_nonUInonIO_browser_threads) { |
+ task_runner_to_create = &process_launcher_task_runner_; |
+ task_runner_traits.WithFileIO() |
+ .WithPriority(base::TaskPriority::USER_BLOCKING) |
+ .WithShutdownBehavior(base::TaskShutdownBehavior::BLOCK_SHUTDOWN); |
+ } else { |
+ thread_to_start = &process_launcher_thread_; |
+ options.timer_slack = base::TIMER_SLACK_MAXIMUM; |
+ } |
break; |
case BrowserThread::CACHE: |
TRACE_EVENT_BEGIN1("startup", |
"BrowserMainLoop::CreateThreads:start", |
"Thread", "BrowserThread::CACHE"); |
- thread_to_start = &cache_thread_; |
#if defined(OS_WIN) |
+ // TaskScheduler doesn't support async I/O on Windows as CACHE thread is |
+ // the only user and this use case is going away in |
+ // https://codereview.chromium.org/2216583003/. TODO(gavinp): Remove |
+ // this ifdef (and thus enable redirection of the CACHE thread on |
+ // Windows) once that CL lands. |
+ thread_to_start = &cache_thread_; |
options = io_message_loop_options; |
-#endif // defined(OS_WIN) |
options.timer_slack = base::TIMER_SLACK_MAXIMUM; |
+#else // OS_WIN |
+ if (redirect_nonUInonIO_browser_threads) { |
+ task_runner_to_create = &cache_task_runner_; |
+ task_runner_traits.WithFileIO() |
+ .WithPriority(base::TaskPriority::USER_BLOCKING) |
+ .WithShutdownBehavior(base::TaskShutdownBehavior::BLOCK_SHUTDOWN); |
+ } else { |
+ thread_to_start = &cache_thread_; |
+ options.timer_slack = base::TIMER_SLACK_MAXIMUM; |
+ } |
+#endif // OS_WIN |
break; |
case BrowserThread::IO: |
TRACE_EVENT_BEGIN1("startup", |
@@ -928,9 +1049,8 @@ int BrowserMainLoop::CreateThreads() { |
options.priority = base::ThreadPriority::DISPLAY; |
#endif |
break; |
- case BrowserThread::UI: |
- case BrowserThread::ID_COUNT: |
- default: |
+ case BrowserThread::UI: // Falls through. |
+ case BrowserThread::ID_COUNT: // Falls through. |
NOTREACHED(); |
break; |
} |
@@ -938,10 +1058,17 @@ int BrowserMainLoop::CreateThreads() { |
BrowserThread::ID id = static_cast<BrowserThread::ID>(thread_id); |
if (thread_to_start) { |
+ DCHECK(!task_runner_to_create); |
(*thread_to_start).reset(new BrowserProcessSubThread(id)); |
if (!(*thread_to_start)->StartWithOptions(options)) { |
LOG(FATAL) << "Failed to start the browser thread: id == " << id; |
} |
+ } else if (task_runner_to_create) { |
+ *task_runner_to_create = |
+ base::CreateSingleThreadTaskRunnerWithTraits(task_runner_traits); |
+ DCHECK(*task_runner_to_create); |
+ BrowserThreadImpl::RedirectThreadIDToTaskRunner( |
+ id, task_runner_to_create->get()); |
} else { |
NOTREACHED(); |
} |
@@ -1089,31 +1216,35 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { |
switch (thread_id) { |
case BrowserThread::DB: { |
TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:DBThread"); |
- ResetThread_DB(std::move(db_thread_)); |
+ ResetThread_DB(std::move(db_thread_), std::move(db_task_runner_)); |
break; |
} |
case BrowserThread::FILE: { |
TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:FileThread"); |
- // Clean up state that lives on or uses the file_thread_ before |
- // it goes away. |
+ // Clean up state that lives on or uses the file_thread_ before it goes |
+ // away. |
save_file_manager_->Shutdown(); |
- ResetThread_FILE(std::move(file_thread_)); |
+ ResetThread_FILE(std::move(file_thread_), std::move(file_task_runner_)); |
break; |
} |
case BrowserThread::FILE_USER_BLOCKING: { |
TRACE_EVENT0("shutdown", |
"BrowserMainLoop::Subsystem:FileUserBlockingThread"); |
- ResetThread_FILE_USER_BLOCKING(std::move(file_user_blocking_thread_)); |
+ ResetThread_FILE_USER_BLOCKING( |
+ std::move(file_user_blocking_thread_), |
+ std::move(file_user_blocking_task_runner_)); |
break; |
} |
case BrowserThread::PROCESS_LAUNCHER: { |
TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:LauncherThread"); |
- ResetThread_PROCESS_LAUNCHER(std::move(process_launcher_thread_)); |
+ ResetThread_PROCESS_LAUNCHER(std::move(process_launcher_thread_), |
+ std::move(process_launcher_task_runner_)); |
break; |
} |
case BrowserThread::CACHE: { |
TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:CacheThread"); |
- ResetThread_CACHE(std::move(cache_thread_)); |
+ ResetThread_CACHE(std::move(cache_thread_), |
+ std::move(cache_task_runner_)); |
break; |
} |
case BrowserThread::IO: { |
@@ -1123,7 +1254,6 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { |
} |
case BrowserThread::UI: |
case BrowserThread::ID_COUNT: |
- default: |
NOTREACHED(); |
break; |
} |
@@ -1361,6 +1491,8 @@ int BrowserMainLoop::BrowserThreadsStarted() { |
allowed_clipboard_threads.push_back(base::PlatformThread::CurrentId()); |
#if defined(OS_WIN) |
// On Windows, clipboards are also used on the FILE or IO threads. |
+ // TODO(gab): Figure out how to selectively allow clipboard via TaskTraits for |
+ // TaskScheduler. http://crbug.com/662055 |
allowed_clipboard_threads.push_back(file_thread_->GetThreadId()); |
allowed_clipboard_threads.push_back(io_thread_->GetThreadId()); |
#endif |