Fix singleton relaunch issues with multi-threaded-message-loop (fixes #3635)

- Fix UI thread shutdown issues on early app exit.
- views: cefclient: Fix threading requirements in RootWindowViews::Init when
  called on the UI thread via OnAlreadyRunningAppRelaunch.
This commit is contained in:
Marshall Greenblatt 2024-01-30 16:33:39 -05:00
parent c4cb1d9fb5
commit ce31761f51
3 changed files with 48 additions and 6 deletions

View File

@ -181,12 +181,20 @@ class CefUIThread : public base::PlatformThread::Delegate {
runner_->RunMessageLoop(); runner_->RunMessageLoop();
// Stop may be called before InitializeBrowserRunner if
// content::ContentMainRun was not successful (for example, due to process
// singleton relaunch).
if (browser_runner_) {
browser_runner_->Shutdown(); browser_runner_->Shutdown();
browser_runner_.reset(); browser_runner_.reset();
}
// This will be a no-op if there is no BrowserTaskExecutor.
content::BrowserTaskExecutor::Shutdown(); content::BrowserTaskExecutor::Shutdown();
if (!shutdown_callback_.is_null()) {
std::move(shutdown_callback_).Run(); std::move(shutdown_callback_).Run();
}
// Run exit callbacks on the UI thread to avoid sequence check failures. // Run exit callbacks on the UI thread to avoid sequence check failures.
base::AtExitManager::ProcessCallbacksNow(); base::AtExitManager::ProcessCallbacksNow();
@ -433,6 +441,11 @@ int CefMainRunner::ContentMainRun(bool* initialized,
int* exit_code) { int* exit_code) {
runner->main_delegate_->BeforeUIThreadInitialize(); runner->main_delegate_->BeforeUIThreadInitialize();
*exit_code = content::ContentMainRun(runner->main_runner_.get()); *exit_code = content::ContentMainRun(runner->main_runner_.get());
if (*exit_code != content::RESULT_CODE_NORMAL_EXIT) {
runner->FinishShutdownOnUIThread();
}
event->Signal(); event->Signal();
}, },
base::Unretained(this), base::Unretained(&uithread_startup_event), base::Unretained(this), base::Unretained(&uithread_startup_event),
@ -444,6 +457,13 @@ int CefMainRunner::ContentMainRun(bool* initialized,
// We need to wait until content::ContentMainRun has finished. // We need to wait until content::ContentMainRun has finished.
uithread_startup_event.Wait(); uithread_startup_event.Wait();
if (exit_code != content::RESULT_CODE_NORMAL_EXIT) {
// content::ContentMainRun was not successful (for example, due to process
// singleton relaunch). Stop the UI thread and block until done.
ui_thread_->Stop();
ui_thread_.reset();
}
} else { } else {
*initialized = true; *initialized = true;
main_delegate_->BeforeUIThreadInitialize(); main_delegate_->BeforeUIThreadInitialize();

View File

@ -58,6 +58,11 @@ void ChromeMainRunnerDelegate::BeforeMainThreadRun(
void ChromeMainRunnerDelegate::BeforeMainMessageLoopRun( void ChromeMainRunnerDelegate::BeforeMainMessageLoopRun(
base::RunLoop* run_loop) { base::RunLoop* run_loop) {
// May be nullptr if content::ContentMainRun exits early.
if (!g_browser_process) {
return;
}
// The ScopedKeepAlive instance triggers shutdown logic when released on the // The ScopedKeepAlive instance triggers shutdown logic when released on the
// UI thread before terminating the message loop (e.g. from CefQuitMessageLoop // UI thread before terminating the message loop (e.g. from CefQuitMessageLoop
// or FinishShutdownOnUIThread when running with multi-threaded message loop). // or FinishShutdownOnUIThread when running with multi-threaded message loop).
@ -67,12 +72,17 @@ void ChromeMainRunnerDelegate::BeforeMainMessageLoopRun(
// The QuitClosure will be executed from BrowserProcessImpl::Unpin() via // The QuitClosure will be executed from BrowserProcessImpl::Unpin() via
// KeepAliveRegistry when the last ScopedKeepAlive is released. // KeepAliveRegistry when the last ScopedKeepAlive is released.
// ScopedKeepAlives are also held by Browser objects. // ScopedKeepAlives are also held by Browser objects.
DCHECK(g_browser_process);
static_cast<BrowserProcessImpl*>(g_browser_process) static_cast<BrowserProcessImpl*>(g_browser_process)
->SetQuitClosure(run_loop->QuitClosure()); ->SetQuitClosure(run_loop->QuitClosure());
} }
bool ChromeMainRunnerDelegate::HandleMainMessageLoopQuit() { bool ChromeMainRunnerDelegate::HandleMainMessageLoopQuit() {
// May be nullptr if content::ContentMainRun exits early.
if (!g_browser_process) {
// Proceed with direct execution of the QuitClosure().
return false;
}
// May be called multiple times. See comments in RunMainMessageLoopBefore. // May be called multiple times. See comments in RunMainMessageLoopBefore.
keep_alive_.reset(); keep_alive_.reset();

View File

@ -58,8 +58,20 @@ void RootWindowViews::Init(RootWindow::Delegate* delegate,
CreateClientHandler(config_->url); CreateClientHandler(config_->url);
initialized_ = true; initialized_ = true;
if (!CURRENTLY_ON_MAIN_THREAD()) {
// Execute GetRequestContext() on the main thread.
MAIN_POST_CLOSURE(base::BindOnce(
[](scoped_refptr<RootWindowViews> self,
const CefBrowserSettings& settings) {
// Continue initialization on the UI thread.
self->InitOnUIThread(settings,
self->delegate_->GetRequestContext(self.get()));
},
scoped_refptr<RootWindowViews>(this), settings));
} else {
// Continue initialization on the UI thread. // Continue initialization on the UI thread.
InitOnUIThread(settings, delegate_->GetRequestContext(this)); InitOnUIThread(settings, delegate_->GetRequestContext(this));
}
} }
void RootWindowViews::InitAsPopup(RootWindow::Delegate* delegate, void RootWindowViews::InitAsPopup(RootWindow::Delegate* delegate,