Commit 066637cf authored by Xi Han's avatar Xi Han Committed by Commit Bot

[Servicification] Create BrowserThread::IO earlier.

In this CL, we split the creation of the BrowserThread::IO thread and
initialization of its delegate:

1. The BrowserThread::IO is created in the
   BrowserMainLoop::PostMainMessageLoopStart() after the UI thread
   is created. Adjust the tracing events in the BrowserMainLoop.
2. Simplify the logic in BrowserMainLoop::CreateThreads() since only
   the launcher thread is created there. Initializes the
   delegate of the BrowserThread::IO there.
3. Update the logic in the BrowserThreadImpl when checking whether
   the BrowserThread::IO has initialized.
4. Update BrowserMainLoopTest and BrowserProcessImplTest.

 This is the first step of starting ServiceManager earlier.

Bug: 729596
Change-Id: I7a08fd7c7bf5548f8039c8c38152ecfde3bd20c4
Reviewed-on: https://chromium-review.googlesource.com/905424
Commit-Queue: Xi Han <hanxi@chromium.org>
Reviewed-by: default avatarKen Rockot <rockot@chromium.org>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#537318}
parent 14fb4bf7
...@@ -49,16 +49,22 @@ class BrowserProcessImplTest : public ::testing::Test { ...@@ -49,16 +49,22 @@ class BrowserProcessImplTest : public ::testing::Test {
g_browser_process = stashed_browser_process_; g_browser_process = stashed_browser_process_;
} }
// Creates the secondary threads (all threads except the UI thread). // Creates the secondary thread (IO thread).
// The UI thread needs to be alive while BrowserProcessImpl is alive, and is // The UI thread needs to be alive while BrowserProcessImpl is alive, and is
// managed separately. // managed separately.
void StartSecondaryThreads() { void StartSecondaryThreads() {
base::TaskScheduler::GetInstance()->StartWithDefaultParams(); base::TaskScheduler::GetInstance()->StartWithDefaultParams();
io_thread_->StartIOThread();
}
// Initializes the IO thread delegate and starts the ServiceManager.
void Initialize() {
io_thread_->InitIOThreadDelegate();
// TestServiceManagerContext creation requires the task scheduler to be // TestServiceManagerContext creation requires the task scheduler to be
// started. // started.
service_manager_context_ = service_manager_context_ =
std::make_unique<content::TestServiceManagerContext>(); std::make_unique<content::TestServiceManagerContext>();
io_thread_->StartIOThread();
} }
// Destroys the secondary threads (all threads except the UI thread). // Destroys the secondary threads (all threads except the UI thread).
...@@ -109,8 +115,11 @@ class BrowserProcessImplTest : public ::testing::Test { ...@@ -109,8 +115,11 @@ class BrowserProcessImplTest : public ::testing::Test {
TEST_F(BrowserProcessImplTest, MAYBE_LifeCycle) { TEST_F(BrowserProcessImplTest, MAYBE_LifeCycle) {
// Setup the BrowserProcessImpl and the threads. // Setup the BrowserProcessImpl and the threads.
browser_process_impl()->Init(); browser_process_impl()->Init();
browser_process_impl()->PreCreateThreads(*command_line()); // A lightweigh IO Thread is created before the
// BrowserThreadImpl::PreCreateThreads is called. https://crbug.com/729596.
StartSecondaryThreads(); StartSecondaryThreads();
browser_process_impl()->PreCreateThreads(*command_line());
Initialize();
browser_process_impl()->PreMainMessageLoopRun(); browser_process_impl()->PreMainMessageLoopRun();
// Force the creation of the NTPResourceCache, to test the destruction order. // Force the creation of the NTPResourceCache, to test the destruction order.
......
...@@ -738,6 +738,11 @@ void BrowserMainLoop::MainMessageLoopStart() { ...@@ -738,6 +738,11 @@ void BrowserMainLoop::MainMessageLoopStart() {
} }
void BrowserMainLoop::PostMainMessageLoopStart() { void BrowserMainLoop::PostMainMessageLoopStart() {
{
TRACE_EVENT0("startup",
"BrowserMainLoop::Subsystem:CreateBrowserThread::IO");
InitializeIOThread();
}
{ {
TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:SystemMonitor"); TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:SystemMonitor");
system_monitor_.reset(new base::SystemMonitor); system_monitor_.reset(new base::SystemMonitor);
...@@ -1034,89 +1039,44 @@ int BrowserMainLoop::CreateThreads() { ...@@ -1034,89 +1039,44 @@ int BrowserMainLoop::CreateThreads() {
base::SequencedWorkerPool::EnableWithRedirectionToTaskSchedulerForProcess(); base::SequencedWorkerPool::EnableWithRedirectionToTaskSchedulerForProcess();
base::Thread::Options io_message_loop_options; TRACE_EVENT_BEGIN1("startup", "BrowserMainLoop::CreateThreads:start",
io_message_loop_options.message_loop_type = base::MessageLoop::TYPE_IO; "Thread", "BrowserThread::PROCESS_LAUNCHER");
// Start threads in the order they occur in the BrowserThread::ID enumeration,
// except for BrowserThread::UI which is the main thread.
//
// Must be size_t so we can increment it.
for (size_t thread_id = BrowserThread::UI + 1;
thread_id < BrowserThread::ID_COUNT;
++thread_id) {
// 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;
// If |message_loop| is not nullptr, then this BrowserThread will use this
// message loop instead of creating a new thread. Note that means this
// thread will not be joined on shutdown, and may cause use-after-free if
// anything tries to access objects deleted by AtExitManager, such as
// non-leaky LazyInstance.
base::MessageLoop* message_loop = nullptr;
// Otherwise this thread ID will be backed by a SingleThreadTaskRunner using
// |task_traits| (to be set below instead of |thread_to_start|).
base::TaskTraits task_traits;
switch (thread_id) {
case BrowserThread::PROCESS_LAUNCHER:
TRACE_EVENT_BEGIN1("startup",
"BrowserMainLoop::CreateThreads:start",
"Thread", "BrowserThread::PROCESS_LAUNCHER");
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
// Android specializes Launcher thread so it is accessible in java. // Android specializes Launcher thread so it is accessible in java.
// Note Android never does clean shutdown, so shutdown use-after-free // Note Android never does clean shutdown, so shutdown use-after-free
// concerns are not a problem in practice. // concerns are not a problem in practice.
message_loop = android::LauncherThread::GetMessageLoop(); base::MessageLoop* message_loop = android::LauncherThread::GetMessageLoop();
DCHECK(message_loop); DCHECK(message_loop);
thread_to_start = &process_launcher_thread_; // This BrowserThread will use this message loop instead of creating a new
// thread. Note that means this/ thread will not be joined on shutdown, and
// may cause use-after-free if anything tries to access objects deleted by
// AtExitManager, such as non-leaky LazyInstance.
process_launcher_thread_.reset(new BrowserProcessSubThread(
BrowserThread::PROCESS_LAUNCHER, message_loop));
#else // defined(OS_ANDROID) #else // defined(OS_ANDROID)
// TODO(gab): WithBaseSyncPrimitives() is likely not required here. // This thread ID will be backed by a SingleThreadTaskRunner using
task_traits = {base::MayBlock(), base::WithBaseSyncPrimitives(), // |task_traits|.
base::TaskPriority::USER_BLOCKING, // TODO(gab): WithBaseSyncPrimitives() is likely not required here.
base::TaskShutdownBehavior::BLOCK_SHUTDOWN}; base::TaskTraits task_traits = {base::MayBlock(),
base::WithBaseSyncPrimitives(),
base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN};
scoped_refptr<base::SingleThreadTaskRunner> redirection_task_runner =
base::CreateSingleThreadTaskRunnerWithTraits(
task_traits, base::SingleThreadTaskRunnerThreadMode::DEDICATED);
DCHECK(redirection_task_runner);
BrowserThreadImpl::RedirectThreadIDToTaskRunner(
BrowserThread::PROCESS_LAUNCHER, std::move(redirection_task_runner));
#endif // defined(OS_ANDROID) #endif // defined(OS_ANDROID)
break;
case BrowserThread::IO:
TRACE_EVENT_BEGIN1("startup",
"BrowserMainLoop::CreateThreads:start",
"Thread", "BrowserThread::IO");
thread_to_start = &io_thread_;
options = io_message_loop_options;
#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
// Up the priority of the |io_thread_| as some of its IPCs relate to
// display tasks.
options.priority = base::ThreadPriority::DISPLAY;
#endif
break;
case BrowserThread::UI: // Falls through.
case BrowserThread::ID_COUNT: // Falls through.
NOTREACHED();
break;
}
BrowserThread::ID id = static_cast<BrowserThread::ID>(thread_id); // |io_thread_| is created by |PostMainMessageLoopStart()|, but its
// full initialization is deferred until this point because it requires
if (thread_to_start) { // several dependencies we don't want to depend on so early in startup.
(*thread_to_start) DCHECK(io_thread_);
.reset(message_loop ? new BrowserProcessSubThread(id, message_loop) io_thread_->InitIOThreadDelegate();
: new BrowserProcessSubThread(id));
// Start the thread if an existing |message_loop| wasn't provided.
if (!message_loop && !(*thread_to_start)->StartWithOptions(options))
LOG(FATAL) << "Failed to start the browser thread: id == " << id;
} else {
scoped_refptr<base::SingleThreadTaskRunner> redirection_task_runner =
base::CreateSingleThreadTaskRunnerWithTraits(
task_traits, base::SingleThreadTaskRunnerThreadMode::DEDICATED);
DCHECK(redirection_task_runner);
BrowserThreadImpl::RedirectThreadIDToTaskRunner(
id, std::move(redirection_task_runner));
}
TRACE_EVENT_END0("startup", "BrowserMainLoop::CreateThreads:start"); TRACE_EVENT_END0("startup", "BrowserMainLoop::CreateThreads:start");
}
created_threads_ = true; created_threads_ = true;
return result_code_; return result_code_;
} }
...@@ -1349,6 +1309,10 @@ base::SequencedTaskRunner* BrowserMainLoop::audio_service_runner() { ...@@ -1349,6 +1309,10 @@ base::SequencedTaskRunner* BrowserMainLoop::audio_service_runner() {
return audio_service_runner_.get(); return audio_service_runner_.get();
} }
void BrowserMainLoop::InitializeIOThreadForTesting() {
InitializeIOThread();
}
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
viz::FrameSinkManagerImpl* BrowserMainLoop::GetFrameSinkManager() const { viz::FrameSinkManagerImpl* BrowserMainLoop::GetFrameSinkManager() const {
return frame_sink_manager_impl_.get(); return frame_sink_manager_impl_.get();
...@@ -1696,6 +1660,20 @@ void BrowserMainLoop::MainMessageLoopRun() { ...@@ -1696,6 +1660,20 @@ void BrowserMainLoop::MainMessageLoopRun() {
#endif #endif
} }
void BrowserMainLoop::InitializeIOThread() {
base::Thread::Options options;
options.message_loop_type = base::MessageLoop::TYPE_IO;
#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
// Up the priority of the |io_thread_| as some of its IPCs relate to
// display tasks.
options.priority = base::ThreadPriority::DISPLAY;
#endif
io_thread_.reset(new BrowserProcessSubThread(BrowserThread::IO));
if (!io_thread_->StartWithOptions(options))
LOG(FATAL) << "Failed to start the browser thread: IO";
}
void BrowserMainLoop::InitializeMojo() { void BrowserMainLoop::InitializeMojo() {
if (!parsed_command_line_.HasSwitch(switches::kSingleProcess)) { if (!parsed_command_line_.HasSwitch(switches::kSingleProcess)) {
// Disallow mojo sync calls in the browser process. Note that we allow sync // Disallow mojo sync calls in the browser process. Note that we allow sync
......
...@@ -159,6 +159,8 @@ class CONTENT_EXPORT BrowserMainLoop { ...@@ -159,6 +159,8 @@ class CONTENT_EXPORT BrowserMainLoop {
// through stopping threads to PostDestroyThreads. // through stopping threads to PostDestroyThreads.
void ShutdownThreadsAndCleanUp(); void ShutdownThreadsAndCleanUp();
void InitializeIOThreadForTesting();
int GetResultCode() const { return result_code_; } int GetResultCode() const { return result_code_; }
media::AudioManager* audio_manager() const { return audio_manager_.get(); } media::AudioManager* audio_manager() const { return audio_manager_.get(); }
...@@ -247,6 +249,7 @@ class CONTENT_EXPORT BrowserMainLoop { ...@@ -247,6 +249,7 @@ class CONTENT_EXPORT BrowserMainLoop {
void MainMessageLoopRun(); void MainMessageLoopRun();
void InitializeIOThread();
void InitializeMojo(); void InitializeMojo();
base::FilePath GetStartupTraceFileName( base::FilePath GetStartupTraceFileName(
const base::CommandLine& command_line) const; const base::CommandLine& command_line) const;
......
...@@ -27,6 +27,7 @@ TEST(BrowserMainLoopTest, CreateThreadsInSingleProcess) { ...@@ -27,6 +27,7 @@ TEST(BrowserMainLoopTest, CreateThreadsInSingleProcess) {
*scoped_command_line.GetProcessCommandLine()); *scoped_command_line.GetProcessCommandLine());
BrowserMainLoop browser_main_loop(main_function_params); BrowserMainLoop browser_main_loop(main_function_params);
browser_main_loop.MainMessageLoopStart(); browser_main_loop.MainMessageLoopStart();
browser_main_loop.InitializeIOThreadForTesting();
browser_main_loop.CreateThreads(); browser_main_loop.CreateThreads();
EXPECT_GE(base::TaskScheduler::GetInstance() EXPECT_GE(base::TaskScheduler::GetInstance()
->GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated( ->GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
......
...@@ -131,11 +131,38 @@ struct BrowserThreadGlobals { ...@@ -131,11 +131,38 @@ struct BrowserThreadGlobals {
// by BrowserThreadGlobals, rather by whoever calls // by BrowserThreadGlobals, rather by whoever calls
// BrowserThread::SetIOThreadDelegate. // BrowserThread::SetIOThreadDelegate.
BrowserThreadDelegateAtomicPtr io_thread_delegate = 0; BrowserThreadDelegateAtomicPtr io_thread_delegate = 0;
// This locks protects |is_io_thread_initialized|. Do not read or modify this
// variable without holding this lock.
base::Lock io_thread_lock;
// A flag indicates whether the BrowserThreadDelegate of the BrowserThread::IO
// thread has been initialized.
bool is_io_thread_initialized = false;
}; };
base::LazyInstance<BrowserThreadGlobals>::Leaky base::LazyInstance<BrowserThreadGlobals>::Leaky
g_globals = LAZY_INSTANCE_INITIALIZER; g_globals = LAZY_INSTANCE_INITIALIZER;
void InitIOThreadDelegateOnIOThread() {
BrowserThreadDelegateAtomicPtr delegate =
base::subtle::NoBarrier_Load(&g_globals.Get().io_thread_delegate);
if (delegate)
reinterpret_cast<BrowserThreadDelegate*>(delegate)->Init();
}
bool IsIOThreadInitialized() {
BrowserThreadGlobals& globals = g_globals.Get();
base::AutoLock lock(globals.io_thread_lock);
return globals.is_io_thread_initialized;
}
void SetIsIOThreadInitialized(bool is_io_thread_initialized) {
BrowserThreadGlobals& globals = g_globals.Get();
base::AutoLock lock(globals.io_thread_lock);
globals.is_io_thread_initialized = is_io_thread_initialized;
}
} // namespace } // namespace
BrowserThreadImpl::BrowserThreadImpl(ID identifier) BrowserThreadImpl::BrowserThreadImpl(ID identifier)
...@@ -162,10 +189,9 @@ BrowserThreadImpl::BrowserThreadImpl(ID identifier, ...@@ -162,10 +189,9 @@ BrowserThreadImpl::BrowserThreadImpl(ID identifier,
} }
void BrowserThreadImpl::Init() { void BrowserThreadImpl::Init() {
BrowserThreadGlobals& globals = g_globals.Get();
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
{ {
BrowserThreadGlobals& globals = g_globals.Get();
base::AutoLock lock(globals.lock); base::AutoLock lock(globals.lock);
// |globals| should already have been initialized for |identifier_| in // |globals| should already have been initialized for |identifier_| in
// BrowserThreadImpl::StartWithOptions(). If this isn't the case it's likely // BrowserThreadImpl::StartWithOptions(). If this isn't the case it's likely
...@@ -182,13 +208,6 @@ void BrowserThreadImpl::Init() { ...@@ -182,13 +208,6 @@ void BrowserThreadImpl::Init() {
base::RunLoop::DisallowNestingOnCurrentThread(); base::RunLoop::DisallowNestingOnCurrentThread();
message_loop()->DisallowTaskObservers(); message_loop()->DisallowTaskObservers();
} }
if (identifier_ == BrowserThread::IO) {
BrowserThreadDelegateAtomicPtr delegate =
base::subtle::NoBarrier_Load(&globals.io_thread_delegate);
if (delegate)
reinterpret_cast<BrowserThreadDelegate*>(delegate)->Init();
}
} }
// We disable optimizations for this block of functions so the compiler doesn't // We disable optimizations for this block of functions so the compiler doesn't
...@@ -252,11 +271,12 @@ void BrowserThreadImpl::Run(base::RunLoop* run_loop) { ...@@ -252,11 +271,12 @@ void BrowserThreadImpl::Run(base::RunLoop* run_loop) {
void BrowserThreadImpl::CleanUp() { void BrowserThreadImpl::CleanUp() {
BrowserThreadGlobals& globals = g_globals.Get(); BrowserThreadGlobals& globals = g_globals.Get();
if (identifier_ == BrowserThread::IO) { if (identifier_ == BrowserThread::IO && IsIOThreadInitialized()) {
BrowserThreadDelegateAtomicPtr delegate = BrowserThreadDelegateAtomicPtr delegate =
base::subtle::NoBarrier_Load(&globals.io_thread_delegate); base::subtle::NoBarrier_Load(&globals.io_thread_delegate);
if (delegate) if (delegate)
reinterpret_cast<BrowserThreadDelegate*>(delegate)->CleanUp(); reinterpret_cast<BrowserThreadDelegate*>(delegate)->CleanUp();
SetIsIOThreadInitialized(false);
} }
// Change the state to SHUTDOWN so that PostTaskHelper stops accepting tasks // Change the state to SHUTDOWN so that PostTaskHelper stops accepting tasks
...@@ -290,6 +310,14 @@ void BrowserThreadImpl::ResetGlobalsForTesting(BrowserThread::ID identifier) { ...@@ -290,6 +310,14 @@ void BrowserThreadImpl::ResetGlobalsForTesting(BrowserThread::ID identifier) {
SetIOThreadDelegate(nullptr); SetIOThreadDelegate(nullptr);
} }
void BrowserThreadImpl::InitIOThreadDelegate() {
DCHECK(!IsIOThreadInitialized());
SetIsIOThreadInitialized(true);
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::BindOnce(&InitIOThreadDelegateOnIOThread));
}
BrowserThreadImpl::~BrowserThreadImpl() { BrowserThreadImpl::~BrowserThreadImpl() {
// All Thread subclasses must call Stop() in the destructor. This is // All Thread subclasses must call Stop() in the destructor. This is
// doubly important here as various bits of code check they are on // doubly important here as various bits of code check they are on
...@@ -476,8 +504,13 @@ bool BrowserThread::IsThreadInitialized(ID identifier) { ...@@ -476,8 +504,13 @@ bool BrowserThread::IsThreadInitialized(ID identifier) {
base::AutoLock lock(globals.lock); base::AutoLock lock(globals.lock);
DCHECK_GE(identifier, 0); DCHECK_GE(identifier, 0);
DCHECK_LT(identifier, ID_COUNT); DCHECK_LT(identifier, ID_COUNT);
return globals.states[identifier] == BrowserThreadState::INITIALIZED || bool running =
globals.states[identifier] == BrowserThreadState::RUNNING; globals.states[identifier] == BrowserThreadState::INITIALIZED ||
globals.states[identifier] == BrowserThreadState::RUNNING;
if (identifier != BrowserThread::IO)
return running;
return running && IsIOThreadInitialized();
} }
// static // static
......
...@@ -43,6 +43,10 @@ class CONTENT_EXPORT BrowserThreadImpl : public BrowserThread, ...@@ -43,6 +43,10 @@ class CONTENT_EXPORT BrowserThreadImpl : public BrowserThread,
bool Start(); bool Start();
bool StartWithOptions(const Options& options); bool StartWithOptions(const Options& options);
bool StartAndWaitForTesting(); bool StartAndWaitForTesting();
// Called only by the BrowserThread::IO thread to initialize its
// BrowserThreadDelegate after the thread is created. See
// https://crbug.com/729596.
void InitIOThreadDelegate();
// Redirects tasks posted to |identifier| to |task_runner|. // Redirects tasks posted to |identifier| to |task_runner|.
static void RedirectThreadIDToTaskRunner( static void RedirectThreadIDToTaskRunner(
......
...@@ -101,6 +101,10 @@ bool TestBrowserThread::StartIOThread() { ...@@ -101,6 +101,10 @@ bool TestBrowserThread::StartIOThread() {
return impl_->StartWithOptions(options); return impl_->StartWithOptions(options);
} }
void TestBrowserThread::InitIOThreadDelegate() {
impl_->InitIOThreadDelegate();
}
void TestBrowserThread::Stop() { void TestBrowserThread::Stop() {
impl_->Stop(); impl_->Stop();
} }
......
...@@ -43,6 +43,9 @@ class TestBrowserThread { ...@@ -43,6 +43,9 @@ class TestBrowserThread {
// Starts the thread with an IOThread message loop. // Starts the thread with an IOThread message loop.
bool StartIOThread(); bool StartIOThread();
// Initializes the BrowserThreadDelegate.
void InitIOThreadDelegate();
// Stops the thread. // Stops the thread.
void Stop(); void Stop();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment