Commit f19a89e8 authored by Xi Han's avatar Xi Han Committed by Commit Bot

Creates TaskScheduler and MessageLoop on main thread early.

When ServiceManager starts, it needs to post sequence tasks.
Therefore, we need to create a TaskScheduler and setup the
MessageLoop for the main thread.

Pariculaly for mac, the Mac registaration of NSApp now happens before
EarlyInitialization. The registration has been moved from
BrowserMainParts::PreMainMessageLoopStart() to ContentMainDelegate's
subclasses which implement ContentMainDelegate::PreMainMessageLoopStart().

This is required by a follow up CL:
https://chromium-review.googlesource.com/c/chromium/src/+/1058131/

Change-Id: Ic67dace05b82fc44d6ab6d480ba0fecffe58fbf8
Reviewed-on: https://chromium-review.googlesource.com/1072387Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Reviewed-by: default avatarElly Fong-Jones <ellyjones@chromium.org>
Reviewed-by: default avatarGabriel Charette <gab@chromium.org>
Commit-Queue: Xi Han <hanxi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#567244}
parent 968dfc54
......@@ -76,10 +76,10 @@ int AwBrowserMainParts::PreEarlyInitialization() {
new AwNetworkChangeNotifierFactory());
}
// Android WebView does not use default MessageLoop. It has its own
// Android specific MessageLoop. Also see MainMessageLoopRun.
// Creates a MessageLoop for Android WebView if doesn't yet exist.
DCHECK(!main_message_loop_.get());
main_message_loop_.reset(new base::MessageLoopForUI);
if (!base::MessageLoopCurrent::IsSet())
main_message_loop_.reset(new base::MessageLoopForUI);
base::MessageLoopCurrentForUI::Get()->Start();
return service_manager::RESULT_CODE_NORMAL_EXIT;
}
......
......@@ -1138,3 +1138,9 @@ service_manager::ProcessType ChromeMainDelegate::OverrideProcessType() {
}
return service_manager::ProcessType::kDefault;
}
void ChromeMainDelegate::PreContentInitialization() {
#if defined(OS_MACOSX)
RegisterBrowserCrApp();
#endif
}
......@@ -56,6 +56,7 @@ class ChromeMainDelegate : public content::ContentMainDelegate {
#endif
bool ShouldEnableProfilerRecording() override;
service_manager::ProcessType OverrideProcessType() override;
void PreContentInitialization() override;
content::ContentBrowserClient* CreateContentBrowserClient() override;
content::ContentGpuClient* CreateContentGpuClient() override;
......
......@@ -20,4 +20,7 @@ void CheckUserDataDirPolicy(base::FilePath* user_data_dir);
// process.
void SetUpBundleOverrides();
// Initialize NSApplication.
void RegisterBrowserCrApp();
#endif // CHROME_APP_CHROME_MAIN_MAC_H_
......@@ -14,6 +14,7 @@
#import "base/mac/foundation_util.h"
#import "base/mac/scoped_nsautorelease_pool.h"
#include "base/strings/sys_string_conversions.h"
#import "chrome/browser/chrome_browser_application_mac.h"
#include "chrome/common/chrome_paths_internal.h"
void SetUpBundleOverrides() {
......@@ -24,3 +25,16 @@ void SetUpBundleOverrides() {
NSBundle* base_bundle = chrome::OuterAppBundle();
base::mac::SetBaseBundleID([[base_bundle bundleIdentifier] UTF8String]);
}
void RegisterBrowserCrApp() {
// Tell Cocoa to finish its initialization, which we want to do manually
// instead of calling NSApplicationMain(). The primary reason is that NSAM()
// never returns, which would leave all the objects currently on the stack
// in scoped_ptrs hanging and never cleaned up. We then load the main nib
// directly. The main event loop is run from common code using the
// MessageLoop API, which works out ok for us because it's a wrapper around
// CFRunLoop.
// Initialize NSApplication using the custom subclass.
chrome_browser_application_mac::RegisterBrowserCrApp();
}
......@@ -763,133 +763,6 @@ const char kMissingLocaleDataMessage[] =
} // namespace chrome_browser
#if !defined(OS_ANDROID)
// A TaskRunner that defers tasks until the real task runner is up and running.
// This is used during early initialization, before the real task runner has
// been created. DeferringTaskRunner has the following states.
//
// . kInstalled: the initial state. Tasks are added to |deferred_runner_|. In
// this state this is installed as the active ThreadTaskRunnerHandle.
// . kWaitingForMainToStart: this is no longer the active
// ThreadTaskRunnerHandle, tasks are still deferred though.
// . kMessageLoopRunning: last state, all deferred tasks are flushed to the
// active ThreadTaskRunnerHandle.
//
// The state changes by calling AdvanceToNextState(), and state changes in
// order.
class ChromeBrowserMainParts::DeferringTaskRunner
: public base::SingleThreadTaskRunner {
public:
DeferringTaskRunner() {
deferred_runner_ =
base::MakeRefCounted<base::DeferredSequencedTaskRunner>();
thread_task_runner_handle_ =
std::make_unique<base::ThreadTaskRunnerHandle>(this);
}
bool is_at_end_state() const {
base::AutoLock lock(lock_);
return state_ == State::kMessageLoopRunning;
}
// See description above class for details.
void AdvanceToNextState() {
std::unique_ptr<base::ThreadTaskRunnerHandle> thread_task_runner_handle;
{
base::AutoLock lock(lock_);
switch (state_) {
case State::kInstalled:
state_ = State::kWaitingForMainToStart;
// ~ThreadTaskRunnerHandle calls back to
// RunsTasksInCurrentSequence(), so destruction has to happen when
// the lock isn't held.
thread_task_runner_handle = std::move(thread_task_runner_handle_);
break;
case State::kWaitingForMainToStart:
state_ = State::kMessageLoopRunning;
deferred_runner_->StartWithTaskRunner(
base::ThreadTaskRunnerHandle::Get());
deferred_runner_ = nullptr;
main_thread_task_runner_ = base::ThreadTaskRunnerHandle::Get();
break;
case State::kMessageLoopRunning:
NOTREACHED();
}
}
}
// base::SequencedTaskRunner:
bool PostDelayedTask(const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta delay) override {
base::AutoLock lock(lock_);
switch (state_) {
case State::kInstalled:
case State::kWaitingForMainToStart:
return deferred_runner_->PostDelayedTask(from_here, std::move(task),
delay);
case State::kMessageLoopRunning:
return main_thread_task_runner_->PostDelayedTask(
from_here, std::move(task), delay);
}
NOTREACHED();
return false;
}
bool RunsTasksInCurrentSequence() const override {
base::AutoLock lock(lock_);
switch (state_) {
case State::kInstalled:
case State::kWaitingForMainToStart:
return deferred_runner_->RunsTasksInCurrentSequence();
case State::kMessageLoopRunning:
return main_thread_task_runner_->RunsTasksInCurrentSequence();
}
NOTREACHED();
return false;
}
bool PostNonNestableDelayedTask(const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta delay) override {
base::AutoLock lock(lock_);
switch (state_) {
case State::kInstalled:
case State::kWaitingForMainToStart:
return deferred_runner_->PostNonNestableDelayedTask(
from_here, std::move(task), delay);
case State::kMessageLoopRunning:
return main_thread_task_runner_->PostNonNestableDelayedTask(
from_here, std::move(task), delay);
}
NOTREACHED();
return false;
}
private:
// See class description for details on states.
enum class State {
kInstalled,
kWaitingForMainToStart,
kMessageLoopRunning,
};
~DeferringTaskRunner() override = default;
// |lock_| protects *all* members.
mutable base::Lock lock_;
scoped_refptr<base::DeferredSequencedTaskRunner> deferred_runner_;
scoped_refptr<SingleThreadTaskRunner> main_thread_task_runner_;
std::unique_ptr<base::ThreadTaskRunnerHandle> thread_task_runner_handle_;
State state_ = State::kInstalled;
DISALLOW_COPY_AND_ASSIGN(DeferringTaskRunner);
};
#endif
// BrowserMainParts ------------------------------------------------------------
ChromeBrowserMainParts::ChromeBrowserMainParts(
......@@ -916,11 +789,6 @@ ChromeBrowserMainParts::ChromeBrowserMainParts(
#if !defined(OS_ANDROID)
startup_watcher_ = std::make_unique<StartupTimeBomb>();
shutdown_watcher_ = std::make_unique<ShutdownWatcherHelper>();
// This needs to be created in the constructor as Chrome OS now creates some
// classes that depend upon a ThreadTaskRunnerHandle in
// PreEarlyInitialization().
initial_task_runner_ = base::MakeRefCounted<DeferringTaskRunner>();
#endif // !defined(OS_ANDROID)
}
......@@ -1142,10 +1010,6 @@ void ChromeBrowserMainParts::ToolkitInitialized() {
}
void ChromeBrowserMainParts::PreMainMessageLoopStart() {
#if !defined(OS_ANDROID)
initial_task_runner_->AdvanceToNextState();
#endif
TRACE_EVENT0("startup", "ChromeBrowserMainParts::PreMainMessageLoopStart");
for (size_t i = 0; i < chrome_extra_parts_.size(); ++i)
......@@ -1322,11 +1186,6 @@ int ChromeBrowserMainParts::PreCreateThreadsImpl() {
// Cache first run state early.
first_run::IsChromeFirstRun();
// The real ThreadTaskRunnerHandle has been installed. Flush all the tasks
// and release the ref to |initial_task_runner_| so that it may be destroyed.
initial_task_runner_->AdvanceToNextState();
DCHECK(initial_task_runner_->is_at_end_state());
initial_task_runner_ = nullptr;
#endif // !defined(OS_ANDROID)
PrefService* local_state = browser_process_->local_state();
......
......@@ -59,10 +59,6 @@ class ChromeBrowserMainParts : public content::BrowserMainParts {
#endif
protected:
#if !defined(OS_ANDROID)
class DeferringTaskRunner;
#endif
explicit ChromeBrowserMainParts(const content::MainFunctionParams& parameters,
std::unique_ptr<ui::DataPack> data_pack);
......@@ -211,13 +207,6 @@ class ChromeBrowserMainParts : public content::BrowserMainParts {
base::FilePath user_data_dir_;
#if !defined(OS_ANDROID)
// This TaskRunner is created and the constructor and destroyed in
// PreCreateThreadsImpl(). It's used to queue any tasks scheduled before the
// real task scheduler has been created.
scoped_refptr<DeferringTaskRunner> initial_task_runner_;
#endif
// This is used to store the ui data pack. The data pack is moved when
// resource bundle gets created.
std::unique_ptr<ui::DataPack> service_manifest_data_pack_;
......
......@@ -104,12 +104,13 @@ int ChromeBrowserMainPartsAndroid::PreEarlyInitialization() {
// Android specific MessageLoop.
DCHECK(!main_message_loop_.get());
// Create and start the MessageLoop.
// Create and start the MessageLoop if doesn't yet exist.
// This is a critical point in the startup process.
{
TRACE_EVENT0("startup",
"ChromeBrowserMainPartsAndroid::PreEarlyInitialization:CreateUiMsgLoop");
main_message_loop_.reset(new base::MessageLoopForUI);
if (!base::MessageLoopCurrent::IsSet())
main_message_loop_ = std::make_unique<base::MessageLoopForUI>();
}
{
......
......@@ -84,17 +84,6 @@ int ChromeBrowserMainPartsMac::PreEarlyInitialization() {
singleton_command_line->AppendSwitch(switches::kNoStartupWindow);
}
// Tell Cocoa to finish its initialization, which we want to do manually
// instead of calling NSApplicationMain(). The primary reason is that NSAM()
// never returns, which would leave all the objects currently on the stack
// in scoped_ptrs hanging and never cleaned up. We then load the main nib
// directly. The main event loop is run from common code using the
// MessageLoop API, which works out ok for us because it's a wrapper around
// CFRunLoop.
// Initialize NSApplication using the custom subclass.
chrome_browser_application_mac::RegisterBrowserCrApp();
// If ui_task is not NULL, the app is actually a browser_test.
if (!parameters().ui_task) {
// The browser process only wants to support the language Cocoa will use,
......
......@@ -962,10 +962,28 @@ int ContentMainRunnerImpl::Run() {
// The thread used to start the ServiceManager is handed-off to
// BrowserMain() which may elect to promote it (e.g. to BrowserThread::IO).
if (process_type.empty()) {
if (GetContentClient()->browser()->ShouldCreateTaskScheduler()) {
// Create the TaskScheduler early to allow upcoming code to use
// the post_task.h API. Note: This is okay because RunBrowserProcessMain()
// will soon result in invoking TaskScheduler::GetInstance()->Start().
// The TaskScheduler being started soon is a strict requirement (delaying
// this start would result in posted tasks not running).
base::TaskScheduler::Create("Browser");
}
if (delegate_)
delegate_->PreContentInitialization();
// Create a MessageLoop if one does not already exist for the current
// thread. This thread won't be promoted as BrowserThread::UI until
// BrowserMainLoop::MainMessageLoopStart().
if (!base::MessageLoopCurrentForUI::IsSet())
main_message_loop_ = std::make_unique<base::MessageLoopForUI>();
return RunBrowserProcessMain(main_params, delegate_,
std::move(service_manager_thread_));
}
#endif
#endif // !defined(CHROME_MULTIPLE_DLL_CHILD)
return RunOtherNamedProcessTypeMain(process_type, main_params, delegate_);
}
......@@ -983,6 +1001,9 @@ void ContentMainRunnerImpl::Shutdown() {
delegate_->ProcessExiting(process_type);
}
// The message loop needs to be destroyed before |exit_manager_|.
main_message_loop_.reset();
#if defined(OS_WIN)
#ifdef _CRTDBG_MAP_ALLOC
_CrtDumpMemoryLeaks();
......
......@@ -9,6 +9,7 @@
#include "base/callback_forward.h"
#include "base/memory/scoped_refptr.h"
#include "base/message_loop/message_loop.h"
#include "build/build_config.h"
#include "content/public/app/content_main.h"
#include "content/public/app/content_main_runner.h"
......@@ -81,6 +82,8 @@ class ContentMainRunnerImpl : public ContentMainRunner {
CreatedMainPartsClosure* created_main_parts_closure_ = nullptr;
std::unique_ptr<base::MessageLoop> main_message_loop_;
DISALLOW_COPY_AND_ASSIGN(ContentMainRunnerImpl);
};
......
......@@ -509,9 +509,7 @@ BrowserMainLoop::BrowserMainLoop(const MainFunctionParams& parameters)
g_current_browser_main_loop = this;
if (GetContentClient()->browser()->ShouldCreateTaskScheduler()) {
// Use an empty string as TaskScheduler name to match the suffix of browser
// process TaskScheduler histograms.
base::TaskScheduler::Create("Browser");
DCHECK(base::TaskScheduler::GetInstance());
}
}
......@@ -649,9 +647,8 @@ void BrowserMainLoop::MainMessageLoopStart() {
TRACE_EVENT0("startup", "BrowserMainLoop::MainMessageLoopStart");
// Create a MessageLoop if one does not already exist for the current thread.
if (!base::MessageLoopCurrent::Get())
main_message_loop_.reset(new base::MessageLoopForUI);
if (!base::MessageLoopCurrentForUI::IsSet())
main_message_loop_ = std::make_unique<base::MessageLoopForUI>();
InitializeMainThread();
}
......
......@@ -121,6 +121,8 @@ class CONTENT_EXPORT BrowserMainLoop {
// that return objects which are owned by this class.
static BrowserMainLoop* GetInstance();
// The TaskScheduler instance must exist but not to be started when building
// BrowserMainLoop.
explicit BrowserMainLoop(const MainFunctionParams& parameters);
virtual ~BrowserMainLoop();
......
......@@ -21,7 +21,7 @@ namespace content {
// the number of cores in its foreground pool.
TEST(BrowserMainLoopTest, CreateThreadsInSingleProcess) {
{
base::MessageLoop message_loop;
base::TaskScheduler::Create("Browser");
base::test::ScopedCommandLine scoped_command_line;
scoped_command_line.GetProcessCommandLine()->AppendSwitch(
switches::kSingleProcess);
......
......@@ -125,6 +125,11 @@ class CONTENT_EXPORT ContentMainDelegate {
const base::Closure& quit_closure,
service_manager::BackgroundServiceManager* service_manager);
// Allows the embedder to perform platform-specific initializatioion. For
// example, things that should be done immediately before the creation of the
// main message loop.
virtual void PreContentInitialization() {}
protected:
friend class ContentClientInitializer;
......
......@@ -315,6 +315,7 @@ void BrowserTestBase::SetUp() {
MainFunctionParams params(*command_line);
params.ui_task = ui_task.release();
params.created_main_parts_closure = created_main_parts_closure.release();
base::TaskScheduler::Create("Browser");
// TODO(phajdan.jr): Check return code, http://crbug.com/374738 .
BrowserMain(params, nullptr);
#else
......
......@@ -398,6 +398,12 @@ void ShellMainDelegate::InitializeResourceBundle() {
#endif
}
void ShellMainDelegate::PreContentInitialization() {
#if defined(OS_MACOSX)
RegisterShellCrApp();
#endif
}
ContentBrowserClient* ShellMainDelegate::CreateContentBrowserClient() {
browser_client_.reset(switches::IsRunWebTestsSwitchPresent()
? new LayoutTestContentBrowserClient
......
......@@ -36,6 +36,7 @@ class ShellMainDelegate : public ContentMainDelegate {
#if defined(OS_LINUX)
void ZygoteForked() override;
#endif
void PreContentInitialization() override;
ContentBrowserClient* CreateContentBrowserClient() override;
ContentGpuClient* CreateContentGpuClient() override;
ContentRendererClient* CreateContentRendererClient() override;
......
......@@ -11,6 +11,9 @@ namespace content {
// the expected pixel results on retina capable displays.
void EnsureCorrectResolutionSettings();
// Initializes NSApplication.
void RegisterShellCrApp();
} // namespace content
#endif // CONTENT_SHELL_APP_SHELL_MAIN_DELEGATE_MAC_H_
......@@ -13,6 +13,7 @@
#include "base/mac/scoped_nsobject.h"
#include "content/public/common/content_switches.h"
#include "content/shell/app/paths_mac.h"
#include "content/shell/browser/shell_application_mac.h"
#include "content/shell/common/shell_switches.h"
namespace content {
......@@ -50,4 +51,9 @@ void EnsureCorrectResolutionSettings() {
CHECK(execvp(argv[0], argv));
}
void RegisterShellCrApp() {
// Force the NSApplication subclass to be used.
[ShellCrApplication sharedApplication];
}
} // namespace content
......@@ -9,14 +9,10 @@
#include "base/mac/bundle_locations.h"
#include "base/mac/scoped_nsobject.h"
#include "base/mac/sdk_forward_declarations.h"
#include "content/shell/browser/shell_application_mac.h"
namespace content {
void ShellBrowserMainParts::PreMainMessageLoopStart() {
// Force the NSApplication subclass to be used.
[ShellCrApplication sharedApplication];
base::scoped_nsobject<NSNib> nib(
[[NSNib alloc] initWithNibNamed:@"MainMenu"
bundle:base::mac::FrameworkBundle()]);
......
......@@ -458,6 +458,7 @@ component("headless") {
"lib/browser/headless_web_contents_impl.h",
"lib/headless_content_main_delegate.cc",
"lib/headless_content_main_delegate.h",
"lib/headless_content_main_delegate_mac.mm",
"lib/renderer/headless_content_renderer_client.cc",
"lib/renderer/headless_content_renderer_client.h",
"lib/utility/headless_content_utility_client.cc",
......@@ -542,6 +543,7 @@ if (!is_component_build) {
"lib/browser/headless_web_contents_impl.h",
"lib/headless_content_main_delegate.cc",
"lib/headless_content_main_delegate.h",
"lib/headless_content_main_delegate_mac.mm",
"lib/renderer/headless_content_renderer_client.cc",
"lib/renderer/headless_content_renderer_client.h",
]
......
......@@ -11,8 +11,6 @@
namespace headless {
void HeadlessBrowserMainParts::PreMainMessageLoopStart() {
// Force the NSApplication subclass to be used.
[HeadlessShellCrApplication sharedApplication];
// Force hide dock and menu bar.
[NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
}
......
......@@ -43,6 +43,9 @@ class HEADLESS_EXPORT HeadlessContentMainDelegate
int RunProcess(
const std::string& process_type,
const content::MainFunctionParams& main_function_params) override;
#if defined(OS_MACOSX)
void PreContentInitialization() override;
#endif
content::ContentBrowserClient* CreateContentBrowserClient() override;
content::ContentUtilityClient* CreateContentUtilityClient() override;
content::ContentRendererClient* CreateContentRendererClient() override;
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "headless/lib/headless_content_main_delegate.h"
#include "headless/lib/browser/headless_shell_application_mac.h"
namespace headless {
void HeadlessContentMainDelegate::PreContentInitialization() {
// Force the NSApplication subclass to be used.
[HeadlessShellCrApplication sharedApplication];
}
} // namespace headless
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