• Lukasz Anforowicz's avatar
    Make sure the spare renderer gets properly cleaned up during shutdown. · 2e7a0eaa
    Lukasz Anforowicz authored
    Ensuring proper cleanup of the spare during browser shutdown
    ============================================================
    
    To properly cleanup the spare renderer we need to:
    
    1. Make sure it is subject to the fast shutdown path (even if it is
       still starting).  This way the spare will be subject to the
       fast shutdown initiated from chrome/browser/browser_shutdown.cc:
    
          void OnShutdownStarting(ShutdownType type) {
            ...
            // Call FastShutdown on all of the RenderProcessHosts.  This will be
            // a no-op in some cases, so we still need to go through the normal
            // shutdown path for the ones that didn't exit here.
            g_shutdown_num_processes = 0;
            g_shutdown_num_processes_slow = 0;
            for (content::RenderProcessHost::iterator i(
                    content::RenderProcessHost::AllHostsIterator());
                 !i.IsAtEnd(); i.Advance()) {
              ++g_shutdown_num_processes;
              if (!i.GetCurrentValue()->FastShutdownIfPossible())
                ++g_shutdown_num_processes_slow;
            }
            ...
    
    2. Make sure the SpareRenderProcessHostManager calls CleanUp on the
       spare for which RenderProcessHostObserver::RenderProcessExited is
       called.
    
    
    Enabling fast shutdown for currently-starting renderers
    =======================================================
    
    This CL removes the code that avoids fast shutdown for renderers that
    are currently starting.  This code comes from
    https://codereview.chromium.org/397031/patch/2029/2041 by jam@.
    
    I feel confident that fast shutdown will be handled correctly for
    renderers that are currently starting.  During the fast shutdown
    RenderProcessHostImpl::ProcessDied will be called and reset
    |child_process_launcher_| - this will correctly terminate the process
    regardless of whether the process 1) has already been started or 2) is
    in currently starting:
    
    1. If the process has already been launched, then the destructor of
       ChildProcessLauncher will ensure process termination by eventually
       calling ForceNormalProcessTerminationSync.  This platform-specific
       method will kill the process.
    
    2. If the process is being started, then ChildProcessLauncherHelper will
       eventually (once the process really starts) get the
       PostLaunchOnClientThread callback.  In the PostLaunchOnClientThread
       method we will check if the weak pointer to |child_process_launcher_|
       is still valid and if not, we will also terminate the process via
       ForceNormalProcessTerminationAsync.
    
       I realize that the above explanation depends on
       PostLaunchOnClientThread method being actually called.  I understand
       that this call might not happen if the browser process crashes.  The
       problem of ensuring child process termination in this case seems like
       an orthogonal, separate problem.
    
    Supporting fast shutdown for currently-starting renderers means that
    RenderProcessHostObserver::RenderProcessExited may be called earlier
    (it is called by ProcessDied which is used on the fast shutdown path).
    I think that such an earlier call is okay - I've tweaked the comments
    for RenderProcessHostObserver::RenderProcessReady accordingly (removing
    the mention of RenderProcessExited callback).
    
    
    NOT enabling fast shutdown for not-yet-started or crashed renderers
    ===================================================================
    
    This CL retains the behavior of NOT enabling fast shutdown for
    not-yet-started or crashed renderers.  Such change would break tests
    like ChromeRenderProcessHostTest.CloseAllTabsDuringProcessDied
    on mac_chromium_rel_ng:
    
        [render_process_host_impl.cc(3743)]
        Check failed: !within_process_died_observer_.
            base::debug::StackTrace::StackTrace(...)
            logging::LogMessage::~LogMessage()
            content::RenderProcessHostImpl::ProcessDied(...)
            content::RenderProcessHostImpl::FastShutdownIfPossible(...)
            CloseWebContentses(...)
            TabStripModel::InternalCloseTabs(...)
            TabStripModel::CloseAllTabs()
            WindowDestroyer::RenderProcessGone(...)
            content::WebContentsImpl::RenderViewTerminated(...)
            content::RenderProcessHostImpl::ProcessDied(...)
            non-virtual thunk to content::RenderProcessHostImpl::OnChannelError()
            ...
    
    Additionally, supporting fast shutdown for previously-crashed renderers
    would also introduce a risk that RenderProcessExited might be called
    twice - once when the process crashes and a second time when the process
    is subject to the fast shutdown path.
    
    
    Proper clean-up of a killed spare RenderProcessHost
    ===================================================
    
    SpareRenderProcessHostManager holds the only reference to a spare
    RenderProcessHost* (which otherwise has no IPC routes and a zero
    ref-count).  Before this CL, SpareRenderProcessHostManager would
    respond to RenderProcessHostObserver::RenderProcessExited call by
    dropping the only reference to the spare - this would result in a leak
    of the RenderProcessHost object.  After this CL,
    RenderProcessHost::Cleanup is called, ensuring proper destruction of the
    object in case of a crash or in case of a fast shutdown.
    
    
    Consistent termination status during fast shutdown
    ==================================================
    
    To ensure consistent termination status during fast shutdown
    (i.e.  TERMINATION_STATUS_STILL_RUNNING) we need to tweak
    ChildProcessLauncher::GetChildTerminationStatus to account for processes
    that have not yet started.  Without this change Windows would see the
    default termination status set in the constructor of
    ChildProcessLauncher which would lead to a change in prerender final
    status seen in the following callstack:
    
            prerender::PrerenderContents::SetFinalStatus
            prerender::PrerenderContents::Destroy
            prerender::PrerenderContents::RenderProcessGone
            content::WebContentsImpl::RenderViewTerminated
            content::RenderProcessHostImpl::ProcessDied
            content::RenderProcessHostImpl::FastShutdownIfPossible
            browser_shutdown::OnShutdownStarting
            Browser::OnWindowClosing
            BrowserView::CanClose
            views::Widget::Close
            BrowserCloseManager::CloseBrowsers
            BrowserCloseManager::TryToCloseBrowsers
            chrome::CloseAllBrowsers
            chrome::AttemptExitInternal
    
    and consequently would lead to failures of the following browser tests
    on Windows:
    
        PrerenderBrowserTest.PrerenderQuickQuit
        PrerenderBrowserTest.PrerenderInfiniteLoop
    
    
    Bug: 808114
    Change-Id: I0e1cb47eac0b59866d87b5b3c76aae95e6223760
    Reviewed-on: https://chromium-review.googlesource.com/994290Reviewed-by: default avatarAlex Moshchuk <alexmos@chromium.org>
    Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
    Commit-Queue: Łukasz Anforowicz <lukasza@chromium.org>
    Cr-Commit-Position: refs/heads/master@{#549278}
    2e7a0eaa
render_process_host_impl.cc 162 KB