Fix crashes when running with multi-threaded message loop (issue #2565)

This commit is contained in:
Alexander Guettler 2019-01-08 16:50:55 -08:00 committed by Marshall Greenblatt
parent 7f08159461
commit 27fb4694ed
3 changed files with 71 additions and 24 deletions

View File

@ -24,6 +24,7 @@
#include "base/synchronization/waitable_event.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "content/app/content_service_manager_main_delegate.h"
#include "content/browser/startup_helper.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
@ -123,7 +124,7 @@ int RunAsCrashpadHandler(const base::CommandLine& command_line) {
argv.insert(argv.begin(), command_line.GetProgram().value());
#endif
std::unique_ptr<char* []> argv_as_utf8(new char*[argv.size() + 1]);
std::unique_ptr<char*[]> argv_as_utf8(new char*[argv.size() + 1]);
std::vector<std::string> storage;
storage.reserve(argv.size());
for (size_t i = 0; i < argv.size(); ++i) {
@ -409,11 +410,38 @@ bool CefContext::Initialize(const CefMainArgs& args,
static_cast<ChromeBrowserProcessStub*>(g_browser_process)->Initialize();
// Run the process. Results in a call to CefMainDelegate::RunProcess() which
// will create the browser runner and message loop without blocking.
exit_code = service_manager::MainRun(*sm_main_params_);
if (settings.multi_threaded_message_loop) {
base::WaitableEvent uithread_startup_event(
base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED);
initialized_ = true;
// This is required because creating a base::Thread as starting it will
// check some feature flags this will cause a CHECK failure later when this
// gets called by some call down the line of service_manager::MainRun.
content::SetUpFieldTrialsAndFeatureList();
if (!main_delegate_->CreateUIThread()) {
return false;
}
initialized_ = true;
// Can't use CEF_POST_TASK here yet, because the TaskRunner is not yet set.
main_delegate_->ui_thread()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(
[](CefContext* context, base::WaitableEvent* event) {
service_manager::MainRun(*context->sm_main_params_);
event->Signal();
},
base::Unretained(this), base::Unretained(&uithread_startup_event)));
// We need to wait until service_manager::MainRun has finished.
uithread_startup_event.Wait();
} else {
initialized_ = true;
service_manager::MainRun(*sm_main_params_);
}
if (CEF_CURRENTLY_ON_UIT()) {
OnContextInitialized();

View File

@ -259,21 +259,24 @@ bool IsScaleFactorSupported(ui::ScaleFactor scale_factor) {
// Used to run the UI on a separate thread.
class CefUIThread : public base::Thread {
public:
CefUIThread(const content::MainFunctionParams& main_function_params)
: base::Thread("CefUIThread"),
main_function_params_(main_function_params) {}
CefUIThread() : base::Thread("CefUIThread") {}
void Init() override {
#if defined(OS_WIN)
// Initializes the COM library on the current thread.
CoInitialize(NULL);
#endif
}
void InitializeBrowserRunner(
const content::MainFunctionParams& main_function_params) {
DCHECK(task_runner()->BelongsToCurrentThread());
// Use our own browser process runner.
browser_runner_.reset(content::BrowserMainRunner::Create());
// Initialize browser process state. Uses the current thread's mesage loop.
int exit_code = browser_runner_->Initialize(main_function_params_);
// Initialize browser process state. Uses the current thread's message loop.
int exit_code = browser_runner_->Initialize(main_function_params);
CHECK_EQ(exit_code, -1);
}
@ -298,7 +301,6 @@ class CefUIThread : public base::Thread {
}
protected:
content::MainFunctionParams main_function_params_;
std::unique_ptr<content::BrowserMainRunner> browser_runner_;
};
@ -315,8 +317,10 @@ CefMainDelegate::CefMainDelegate(CefRefPtr<CefApp> application)
CefMainDelegate::~CefMainDelegate() {}
void CefMainDelegate::PreCreateMainMessageLoop() {
// Create the main message loop.
message_loop_.reset(new CefBrowserMessageLoop());
if (!message_loop_) {
// Create the main message loop.
message_loop_.reset(new CefBrowserMessageLoop());
}
}
bool CefMainDelegate::BasicStartupComplete(int* exit_code) {
@ -591,17 +595,10 @@ int CefMainDelegate::RunProcess(
if (exit_code >= 0)
return exit_code;
} else {
// Run the UI on a separate thread.
std::unique_ptr<base::Thread> thread;
thread.reset(new CefUIThread(main_function_params));
base::Thread::Options options;
options.message_loop_type = base::MessageLoop::TYPE_UI;
if (!thread->StartWithOptions(options)) {
NOTREACHED() << "failed to start UI thread";
return 1;
}
thread->WaitUntilThreadStarted();
ui_thread_.swap(thread);
// Running on the separate UI thread.
DCHECK(ui_thread_);
static_cast<CefUIThread*>(ui_thread_.get())
->InitializeBrowserRunner(main_function_params);
}
return 0;
@ -610,6 +607,25 @@ int CefMainDelegate::RunProcess(
return -1;
}
bool CefMainDelegate::CreateUIThread() {
DCHECK(!ui_thread_);
DCHECK(!message_loop_);
std::unique_ptr<base::Thread> thread;
thread.reset(new CefUIThread());
base::Thread::Options options;
options.message_loop_type = base::MessageLoop::TYPE_UI;
if (!thread->StartWithOptions(options)) {
NOTREACHED() << "failed to start UI thread";
return false;
}
thread->WaitUntilThreadStarted();
ui_thread_.swap(thread);
message_loop_.reset(new CefBrowserMessageLoop());
return true;
}
void CefMainDelegate::ProcessExiting(const std::string& process_type) {
ui::ResourceBundle::CleanupSharedInstance();
}

View File

@ -48,11 +48,14 @@ class CefMainDelegate : public content::ContentMainDelegate {
content::ContentRendererClient* CreateContentRendererClient() override;
content::ContentUtilityClient* CreateContentUtilityClient() override;
bool CreateUIThread();
// Shut down the browser runner.
void ShutdownBrowser();
CefContentBrowserClient* browser_client() { return browser_client_.get(); }
CefContentClient* content_client() { return &content_client_; }
base::Thread* ui_thread() { return ui_thread_.get(); }
private:
void InitializeResourceBundle();