Changeset 249703 in webkit


Ignore:
Timestamp:
Sep 9, 2019 9:24:23 PM (5 years ago)
Author:
Chris Dumez
Message:

[iOS] We sometimes attempt to use a terminated prewarmed WebContent process
https://bugs.webkit.org/show_bug.cgi?id=201614
<rdar://problem/54714507>

Reviewed by Geoffrey Garen.

On iOS, it is possible for our processes to get terminated (e.g. jetsammed) while the UIProcess
is suspended. Upon resuming, it takes a little while for the UIProcess to get the notification
that the mac connection to its child process has been severed and the UIProcess may try to use
it for a load. This is especially problematic for prewarmed process because the client will end
up showing a crash banner and reloading when we could have used a new process rather the prewarmed
one if we had known it was dead.

This patch makes 2 improvements:

  1. It makes AuxiliaryProcessProxy::state() return Terminated if we still have a connection but the PID is not the PID of a running process. I also added a check in tryTakePrewarmedProcess() to not use the prewarmed process if it state() is Terminated.
  2. When the UIProcess is about to get suspended, have the process pools terminate their non-critical processes (i.e. prewarmed + the ones used for PageCache). This makes WebKit friendlier with other apps on the system when suspended with regards to memory. Also, it makes it less likely useful WebContent processes will get jetsammed.
  • UIProcess/AuxiliaryProcessProxy.cpp:

(WebKit::AuxiliaryProcessProxy::state const):
(WebKit::AuxiliaryProcessProxy::isRunningProcessPID):

  • UIProcess/AuxiliaryProcessProxy.h:
  • UIProcess/Cocoa/WebProcessPoolCocoa.mm:

(WebKit::WebProcessPool::applicationIsAboutToSuspend):

  • UIProcess/WebProcessPool.cpp:

(WebKit::WebProcessPool::tryTakePrewarmedProcess):

  • UIProcess/WebProcessPool.h:
  • UIProcess/ios/ProcessAssertionIOS.mm:

(-[WKProcessAssertionBackgroundTaskManager init]):
(-[WKProcessAssertionBackgroundTaskManager _releaseBackgroundTask]):

Location:
trunk/Source/WebKit
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r249702 r249703  
     12019-09-09  Chris Dumez  <cdumez@apple.com>
     2
     3        [iOS] We sometimes attempt to use a terminated prewarmed WebContent process
     4        https://bugs.webkit.org/show_bug.cgi?id=201614
     5        <rdar://problem/54714507>
     6
     7        Reviewed by Geoffrey Garen.
     8
     9        On iOS, it is possible for our processes to get terminated (e.g. jetsammed) while the UIProcess
     10        is suspended. Upon resuming, it takes a little while for the UIProcess to get the notification
     11        that the mac connection to its child process has been severed and the UIProcess may try to use
     12        it for a load. This is especially problematic for prewarmed process because the client will end
     13        up showing a crash banner and reloading when we could have used a new process rather the prewarmed
     14        one if we had known it was dead.
     15
     16        This patch makes 2 improvements:
     17        1. It makes AuxiliaryProcessProxy::state() return Terminated if we still have a connection but
     18           the PID is not the PID of a running process. I also added a check in tryTakePrewarmedProcess()
     19           to not use the prewarmed process if it state() is Terminated.
     20        2. When the UIProcess is about to get suspended, have the process pools terminate their non-critical
     21           processes (i.e. prewarmed + the ones used for PageCache). This makes WebKit friendlier with
     22           other apps on the system when suspended with regards to memory. Also, it makes it less likely
     23           useful WebContent processes will get jetsammed.
     24
     25        * UIProcess/AuxiliaryProcessProxy.cpp:
     26        (WebKit::AuxiliaryProcessProxy::state const):
     27        (WebKit::AuxiliaryProcessProxy::isRunningProcessPID):
     28        * UIProcess/AuxiliaryProcessProxy.h:
     29        * UIProcess/Cocoa/WebProcessPoolCocoa.mm:
     30        (WebKit::WebProcessPool::applicationIsAboutToSuspend):
     31        * UIProcess/WebProcessPool.cpp:
     32        (WebKit::WebProcessPool::tryTakePrewarmedProcess):
     33        * UIProcess/WebProcessPool.h:
     34        * UIProcess/ios/ProcessAssertionIOS.mm:
     35        (-[WKProcessAssertionBackgroundTaskManager init]):
     36        (-[WKProcessAssertionBackgroundTaskManager _releaseBackgroundTask]):
     37
    1382019-09-09  Chris Dumez  <cdumez@apple.com>
    239
  • trunk/Source/WebKit/UIProcess/AuxiliaryProcessProxy.cpp

    r249649 r249703  
    2929#include "AuxiliaryProcessMessages.h"
    3030#include "LoadParameters.h"
     31#include "Logging.h"
    3132#include "WebPageMessages.h"
    3233#include <wtf/RunLoop.h>
     
    109110        return AuxiliaryProcessProxy::State::Launching;
    110111
    111     if (!m_connection)
     112    // There is sometimes a delay until we get the notification from mach about the connection getting closed.
     113    // To help detect terminated process earlier, we also check that the PID is for a valid running process.
     114    if (!m_connection || !isRunningProcessPID(processIdentifier()))
    112115        return AuxiliaryProcessProxy::State::Terminated;
    113116
    114117    return AuxiliaryProcessProxy::State::Running;
     118}
     119
     120bool AuxiliaryProcessProxy::isRunningProcessPID(ProcessID pid)
     121{
     122    if (!pid)
     123        return false;
     124
     125#if PLATFORM(COCOA)
     126    // Use kill() with a signal of 0 to check if there is actually still a process with the given PID.
     127    if (!kill(pid, 0))
     128        return true;
     129
     130    if (errno == ESRCH) {
     131        // No process can be found corresponding to that specified by pid.
     132        return false;
     133    }
     134
     135    RELEASE_LOG_ERROR(Process, "kill() returned unexpected error %d", errno);
     136    return true;
     137#else
     138    UNUSED_PARAM(pid);
     139    return true;
     140#endif
    115141}
    116142
  • trunk/Source/WebKit/UIProcess/AuxiliaryProcessProxy.h

    r248846 r249703  
    124124    virtual void connectionWillOpen(IPC::Connection&);
    125125    virtual void processWillShutDown(IPC::Connection&) = 0;
     126    static bool isRunningProcessPID(ProcessID);
    126127
    127128    Vector<std::pair<std::unique_ptr<IPC::Encoder>, OptionSet<IPC::SendOption>>> m_pendingMessages;
  • trunk/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm

    r249684 r249703  
    3030#import "CookieStorageUtilsCF.h"
    3131#import "LegacyCustomProtocolManagerClient.h"
     32#import "Logging.h"
    3233#import "NetworkProcessCreationParameters.h"
    3334#import "NetworkProcessMessages.h"
     
    581582}
    582583
     584#if PLATFORM(IOS_FAMILY)
     585void WebProcessPool::applicationIsAboutToSuspend()
     586{
     587    RELEASE_LOG(ProcessSuspension, "Application is about to suspend so we simulate memory pressure to terminate non-critical processes");
     588    // Simulate memory pressure handling so free as much memory as possible before suspending.
     589    // In particular, this will terminate prewarmed and PageCache processes.
     590    for (auto* processPool : allProcessPools())
     591        processPool->handleMemoryPressureWarning(Critical::Yes);
     592}
     593#endif
     594
    583595} // namespace WebKit
  • trunk/Source/WebKit/UIProcess/WebProcessPool.cpp

    r249671 r249703  
    826826    if (!m_prewarmedProcess)
    827827        return nullptr;
     828   
     829    // There is sometimes a delay until we get notified that a prewarmed process has been terminated (e.g. after resuming
     830    // from suspension) so make sure the process is still running here before deciding to use it.
     831    if (m_prewarmedProcess->state() == AuxiliaryProcessProxy::State::Terminated) {
     832        RELEASE_LOG_ERROR(Process, "Not using prewarmed process %d because it has been terminated", m_prewarmedProcess->processIdentifier());
     833        m_prewarmedProcess = nullptr;
     834        return nullptr;
     835    }
    828836
    829837#if PLATFORM(GTK) || PLATFORM(WPE)
  • trunk/Source/WebKit/UIProcess/WebProcessPool.h

    r249671 r249703  
    223223    void populateVisitedLinks();
    224224
     225#if PLATFORM(IOS_FAMILY)
     226    static void applicationIsAboutToSuspend();
     227#endif
     228
    225229    void handleMemoryPressureWarning(Critical);
    226230
  • trunk/Source/WebKit/UIProcess/ios/ProcessAssertionIOS.mm

    r247109 r249703  
    3131#import "AssertionServicesSPI.h"
    3232#import "Logging.h"
     33#import "WebProcessPool.h"
    3334#import <UIKit/UIApplication.h>
    3435#import <wtf/HashMap.h>
     
    8283    [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:[UIApplication sharedApplication] queue:nil usingBlock:^(NSNotification *) {
    8384        _applicationIsBackgrounded = YES;
     85       
     86        if (_backgroundTask == UIBackgroundTaskInvalid)
     87            WebKit::WebProcessPool::applicationIsAboutToSuspend();
    8488    }];
    8589
     
    184188
    185189    RELEASE_LOG(ProcessSuspension, "%p - WKProcessAssertionBackgroundTaskManager - endBackgroundTask", self);
     190    if (_applicationIsBackgrounded)
     191        WebKit::WebProcessPool::applicationIsAboutToSuspend();
    186192    [[UIApplication sharedApplication] endBackgroundTask:_backgroundTask];
    187193    _backgroundTask = UIBackgroundTaskInvalid;
Note: See TracChangeset for help on using the changeset viewer.