[TOC]
This documents shutdown steps on Windows, Mac and Linux.
On Android, the system can terminate the Chrome app at any point without running any shutdown step.
See below for how the process differs on ChromeOS.
Since M98, Chrome can destroy Profile
objects separately from shutdown; on
Windows and Linux, this happens in multi-profile scenarios. On macOS, it can
also happen in single-profile scenarios, because Chrome lifetime is separate
from browser windows.
Typically, this logic triggers when all browser windows are closed, but other
things can keep a Profile
alive.
~ScopedProfileKeepAlive
posts a task to run RemoveKeepAliveOnUIThread
. This
decrements the refcount in ProfileManager
, and if it hits zero then
DestroyProfileWhenAppropriate
is called.
ProfileDestroyer::DestroyProfileWhenAppropriate
...
ProfileManager::RemoveProfile
ProfileManager::RemoveKeepAlive
ScopedProfileKeepAlive::RemoveKeepAliveOnUIThread
Unlike regular profiles, OTR profiles are not refcounted. Instead,
~Browser
checks the profile's browser count after removing itself. If it's
zero, it calls DestroyProfileWhenAppropriate
directly.
ProfileDestroyer::DestroyProfileWhenAppropriate
Browser::~Browser
You can use ProfileManager
logging to inspect a profile's keepalive state:
$ ./out/Default/chrome --enable-logging=stderr --v=0 --vmodule=profile_manager=1
[71002:259:0328/133310.430142:VERBOSE1:profile_manager.cc(1489)] AddKeepAlive(Default, kBrowserWindow). keep_alives=[kWaitingForFirstBrowserWindow (1), kBrowserWindow (1)]
[71002:259:0328/133310.430177:VERBOSE1:profile_manager.cc(1543)] ClearFirstBrowserWindowKeepAlive(Default). keep_alives=[kBrowserWindow (1)]
[71002:259:0328/133314.468135:VERBOSE1:profile_manager.cc(1489)] AddKeepAlive(Default, kExtensionUpdater). keep_alives=[kBrowserWindow (1), kExtensionUpdater (1)]
[71002:259:0328/133314.469444:VERBOSE1:profile_manager.cc(1522)] RemoveKeepAlive(Default, kExtensionUpdater). keep_alives=[kBrowserWindow (1)]
[71002:259:0328/133315.396614:VERBOSE1:profile_manager.cc(1489)] AddKeepAlive(Default, kOffTheRecordProfile). keep_alives=[kBrowserWindow (1), kOffTheRecordProfile (1)]
[71002:259:0328/133417.078148:VERBOSE1:profile_manager.cc(1522)] RemoveKeepAlive(Default, kBrowserWindow). keep_alives=[kOffTheRecordProfile (1)]
[71002:259:0328/133442.705250:VERBOSE1:profile_manager.cc(1522)] RemoveKeepAlive(Default, kOffTheRecordProfile). keep_alives=[]
[71002:259:0328/133442.705296:VERBOSE1:profile_manager.cc(1567)] Deleting profile Default
Shutdown starts when nothing keeps Chrome alive. Typically, this happens when all browser windows are closed, but other things can keep Chrome alive.
When nothing keeps Chrome alive, BrowserProcessImpl::Unpin
asks the main
thread's message loop to quit as soon as it no longer has tasks ready to run
immediately.
base::RunLoop::QuitWhenIdle
…
BrowserProcessImpl::Unpin
BrowserProcessImpl::OnKeepAliveStateChanged
KeepAliveRegistry::OnKeepAliveStateChanged
KeepAliveRegistry::Unregister
ScopedKeepAlive::~ScopedKeepAlive
...
Browser::UnregisterKeepAlive
BrowserList::RemoveBrowser
Browser::~Browser
Following this request, ChromeBrowserMainParts::MainMessageLoopRun
exits. Tasks
posted to the main thread without a delay prior to this point are guaranteed to
have run; tasks posted to the main thread after this point will never run.
BrowserMainRunnerImpl::Shutdown
is called on the main thread. Within that
method, BrowserMainLoop::ShutdownThreadsAndCleanUp
orchestrates the main
shutdown steps.
ChromeBrowserMainParts::PostMainMessageLoopRun
is invoked. It invokes the
PostMainMessageLoopRun
method of each ChromeBrowserMainExtraParts
instance.
This is a good place to perform shutdown steps of a component that require the
IO thread, the ThreadPool
or the Profile
to still be available.
ChromeBrowserMainParts::PostMainMessageLoopRun
also invokes
BrowserProcessImpl::StartTearDown
which deletes many services owned by
BrowserProcessImpl
(aka g_browser_process
). One of these services is the
ProfileManager
. Deleting the ProfileManager
deletes Profiles
. As part of
deleting a Profile
, its KeyedServices
are deleted, including:
- Sync Service
- History Service
The IO thread is joined. No IPC or Mojo can be received after this.
ThreadPool
shutdown starts. At this point, no new SKIP_ON_SHUTDOWN
or
CONTINUE_ON_SHUTDOWN
task can start running (they are deleted without
running). The main thread blocks until all SKIP_ON_SHUTDOWN
tasks that started
running prior to ThreadPool
shutdown start are complete, and all
BLOCK_SHUTDOWN
tasks are complete (irrespective of whether they were posted
before or after ThreadPool
shutdown start). When no more SKIP_ON_SHUTDOWN
is
running and no more BLOCK_SHUTDOWN
task is queued or running, the main thread
is unblocked and ThreadPool
shutdown is considered complete. Note:
CONTINUE_ON_SHUTDOWN
tasks that started before ThreadPool
shutdown may still
be running.
At this point, new tasks posted to the IO thread or to the ThreadPool
cannot
run. It is illegal to post a BLOCK_SHUTDOWN
task to the ThreadPool
(enforced
by a DCHECK
).
ChromeBrowserMainParts::PostDestroyThreads
is invoked. It invokes
BrowserProcessImpl::PostDestroyThreads
. Since it is guaranteed that no
SKIP_ON_SHUTDOWN
or BLOCK_SHUTDOWN
task is running at this point, it is a
good place to delete objects accessed directly from these tasks.
Then, if a new Chrome executable, it is swapped with the current one (Windows-only).
upgrade_util::SwapNewChromeExeIfPresent
browser_shutdown::ShutdownPostThreadsStop
ChromeBrowserMainParts::PostDestroyThreads
content::BrowserMainLoop::ShutdownThreadsAndCleanUp
content::BrowserMainLoop::ShutdownThreadsAndCleanUp
content::BrowserMainRunnerImpl::Shutdown
On ChromeOS, the ash browser is only supposed to exit when the user logs out.
When the user logs out, the browser sends a StopSession
message to the
session_manager.
The session_manager then sends a SIGTERM to the main browser process to cause an
exit. Once SIGTERM is received, it starts shutting down the main loop and
cleaning up in the sequence described above.
Unlike other desktop platforms, the shutdown is time limited. If the browser process has not exited within a certain time frame (normally, 3 seconds), the session_manager will SIGKILL the browser process since the user is looking at a blank screen and unable to use their Chromebook until the browser exits.