Diff
Modified: trunk/Source/WebKit/ChangeLog (282217 => 282218)
--- trunk/Source/WebKit/ChangeLog 2021-09-09 16:08:51 UTC (rev 282217)
+++ trunk/Source/WebKit/ChangeLog 2021-09-09 16:40:46 UTC (rev 282218)
@@ -1,3 +1,45 @@
+2021-09-09 Chris Dumez <[email protected]>
+
+ Implement a WebProcess cap
+ https://bugs.webkit.org/show_bug.cgi?id=230067
+ <rdar://79479244>
+
+ Reviewed by Geoffrey Garen.
+
+ Implement a WebProcess cap to avoid getting into a state where we run out of resources and crash in various ways.
+ The current limit is 400 and can be changed via an SPI. The limit is per UIProcess (not per process pool).
+ When we try and launch a new WebProcess and the limit has been reached, we terminate the least recently used
+ WebProcess to avoid going over the limit.
+
+ * Shared/ProcessTerminationReason.h:
+ * UIProcess/API/C/WKAPICast.h:
+ (WebKit::toAPI):
+ * UIProcess/API/Cocoa/WKProcessPool.mm:
+ (+[WKProcessPool _setWebProcessCountLimit:]):
+ * UIProcess/API/Cocoa/WKProcessPoolPrivate.h:
+ * UIProcess/Cocoa/NavigationState.mm:
+ (WebKit::wkProcessTerminationReason):
+ * UIProcess/WebPageProxy.cpp:
+ (WebKit::WebPageProxy::loadRequestWithNavigationShared):
+ (WebKit::WebPageProxy::loadFile):
+ (WebKit::WebPageProxy::loadDataWithNavigationShared):
+ (WebKit::WebPageProxy::loadSimulatedRequest):
+ (WebKit::WebPageProxy::loadAlternateHTML):
+ (WebKit::WebPageProxy::loadWebArchiveData):
+ (WebKit::WebPageProxy::reload):
+ (WebKit::WebPageProxy::goToBackForwardItem):
+ (WebKit::shouldReloadAfterProcessTermination):
+ * UIProcess/WebProcessProxy.cpp:
+ (WebKit::WebProcessProxy::setProcessCountLimit):
+ (WebKit::WebProcessProxy::create):
+ (WebKit::WebProcessProxy::~WebProcessProxy):
+ (WebKit::WebProcessProxy::addProvisionalPageProxy):
+ (WebKit::WebProcessProxy::addExistingWebPage):
+ (WebKit::WebProcessProxy::processDidTerminateOrFailedToLaunch):
+ (WebKit::WebProcessProxy::establishServiceWorkerContext):
+ (WebKit::WebProcessProxy::markProcessAsRecentlyUsed):
+ * UIProcess/WebProcessProxy.h:
+
2021-09-09 Wenson Hsieh <[email protected]>
Unreviewed, fix the Mac Catalyst build after r282191
Modified: trunk/Source/WebKit/Shared/ProcessTerminationReason.h (282217 => 282218)
--- trunk/Source/WebKit/Shared/ProcessTerminationReason.h 2021-09-09 16:08:51 UTC (rev 282217)
+++ trunk/Source/WebKit/Shared/ProcessTerminationReason.h 2021-09-09 16:40:46 UTC (rev 282218)
@@ -30,6 +30,7 @@
enum class ProcessTerminationReason {
ExceededMemoryLimit,
ExceededCPULimit,
+ ExceededProcessCountLimit,
RequestedByClient,
Crash,
NavigationSwap,
Modified: trunk/Source/WebKit/UIProcess/API/C/WKAPICast.h (282217 => 282218)
--- trunk/Source/WebKit/UIProcess/API/C/WKAPICast.h 2021-09-09 16:08:51 UTC (rev 282217)
+++ trunk/Source/WebKit/UIProcess/API/C/WKAPICast.h 2021-09-09 16:40:46 UTC (rev 282218)
@@ -243,6 +243,7 @@
FALLTHROUGH;
case ProcessTerminationReason::RequestedByClient:
return kWKProcessTerminationReasonRequestedByClient;
+ case ProcessTerminationReason::ExceededProcessCountLimit:
case ProcessTerminationReason::RequestedByNetworkProcess:
case ProcessTerminationReason::RequestedByGPUProcess:
case ProcessTerminationReason::Crash:
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKProcessPool.mm (282217 => 282218)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKProcessPool.mm 2021-09-09 16:08:51 UTC (rev 282217)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKProcessPool.mm 2021-09-09 16:40:46 UTC (rev 282218)
@@ -565,6 +565,11 @@
#endif
}
++ (void)_setWebProcessCountLimit:(unsigned)limit
+{
+ WebKit::WebProcessProxy::setProcessCountLimit(limit);
+}
+
- (void)_garbageCollectJavaScriptObjectsForTesting
{
_processPool->garbageCollectJavaScriptObjects();
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKProcessPoolPrivate.h (282217 => 282218)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKProcessPoolPrivate.h 2021-09-09 16:08:51 UTC (rev 282217)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKProcessPoolPrivate.h 2021-09-09 16:40:46 UTC (rev 282218)
@@ -73,6 +73,8 @@
+ (NSURL *)_websiteDataURLForContainerWithURL:(NSURL *)containerURL bundleIdentifierIfNotInContainer:(NSString *)bundleIdentifier;
+ (pid_t)_webAuthnProcessIdentifier WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
++ (void)_setWebProcessCountLimit:(unsigned)limit WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
- (void)_warmInitialProcess WK_API_AVAILABLE(macos(10.12), ios(10.0));
- (void)_automationCapabilitiesDidChange WK_API_AVAILABLE(macos(10.12), ios(10.0));
- (void)_setAutomationSession:(_WKAutomationSession *)automationSession WK_API_AVAILABLE(macos(10.12), ios(10.0));
Modified: trunk/Source/WebKit/UIProcess/Cocoa/NavigationState.mm (282217 => 282218)
--- trunk/Source/WebKit/UIProcess/Cocoa/NavigationState.mm 2021-09-09 16:08:51 UTC (rev 282217)
+++ trunk/Source/WebKit/UIProcess/Cocoa/NavigationState.mm 2021-09-09 16:40:46 UTC (rev 282218)
@@ -1060,6 +1060,7 @@
FALLTHROUGH;
case ProcessTerminationReason::RequestedByClient:
return _WKProcessTerminationReasonRequestedByClient;
+ case ProcessTerminationReason::ExceededProcessCountLimit:
case ProcessTerminationReason::RequestedByNetworkProcess:
case ProcessTerminationReason::RequestedByGPUProcess:
case ProcessTerminationReason::Crash:
Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (282217 => 282218)
--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp 2021-09-09 16:08:51 UTC (rev 282217)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp 2021-09-09 16:40:46 UTC (rev 282218)
@@ -1413,6 +1413,8 @@
navigation.setIsLoadedWithNavigationShared(true);
+ process->markProcessAsRecentlyUsed();
+
if (!process->isLaunching() || !url.isLocalFile())
process->send(Messages::WebPage::LoadRequest(loadParameters), webPageID);
else
@@ -1474,6 +1476,7 @@
maybeInitializeSandboxExtensionHandle(m_process, fileURL, resourceDirectoryURL, loadParameters.sandboxExtensionHandle, checkAssumedReadAccessToResourceURL);
addPlatformLoadParameters(m_process, loadParameters);
+ m_process->markProcessAsRecentlyUsed();
if (m_process->isLaunching())
send(Messages::WebPage::LoadRequestWaitingForProcessLaunch(loadParameters, resourceDirectoryURL, m_identifier, checkAssumedReadAccessToResourceURL));
else
@@ -1533,6 +1536,7 @@
loadParameters.isNavigatingToAppBoundDomain = isNavigatingToAppBoundDomain;
addPlatformLoadParameters(process, loadParameters);
+ process->markProcessAsRecentlyUsed();
process->assumeReadAccessToBaseURL(*this, baseURL);
process->send(Messages::WebPage::LoadData(loadParameters), webPageID);
process->startResponsivenessTimer();
@@ -1590,6 +1594,7 @@
addPlatformLoadParameters(m_process, loadParameters);
+ m_process->markProcessAsRecentlyUsed();
m_process->assumeReadAccessToBaseURL(*this, baseURL);
m_process->send(Messages::WebPage::LoadSimulatedRequestAndResponse(loadParameters, simulatedResponse), m_webPageID);
m_process->startResponsivenessTimer();
@@ -1633,6 +1638,7 @@
loadParameters.userData = UserData(process().transformObjectsToHandles(userData).get());
addPlatformLoadParameters(process(), loadParameters);
+ m_process->markProcessAsRecentlyUsed();
m_process->assumeReadAccessToBaseURL(*this, baseURL.string());
m_process->assumeReadAccessToBaseURL(*this, unreachableURL.string());
send(Messages::WebPage::LoadAlternateHTML(loadParameters));
@@ -1662,6 +1668,7 @@
loadParameters.userData = UserData(process().transformObjectsToHandles(userData).get());
addPlatformLoadParameters(process(), loadParameters);
+ m_process->markProcessAsRecentlyUsed();
send(Messages::WebPage::LoadData(loadParameters));
m_process->startResponsivenessTimer();
}
@@ -1737,6 +1744,7 @@
if (options.contains(WebCore::ReloadOption::DisableContentBlockers))
navigation->setUserContentExtensionsEnabled(false);
+ m_process->markProcessAsRecentlyUsed();
send(Messages::WebPage::Reload(navigation->navigationID(), options.toRaw(), sandboxExtensionHandle));
m_process->startResponsivenessTimer();
@@ -1815,6 +1823,7 @@
auto transaction = m_pageLoadState.transaction();
m_pageLoadState.setPendingAPIRequest(transaction, { navigation ? navigation->navigationID() : 0, item.url() });
+ m_process->markProcessAsRecentlyUsed();
send(Messages::WebPage::GoToBackForwardItem(navigation ? navigation->navigationID() : 0, item.itemID(), frameLoadType, ShouldTreatAsContinuingLoad::No, std::nullopt, m_lastNavigationWasAppInitiated));
m_process->startResponsivenessTimer();
@@ -5176,6 +5185,7 @@
void WebPageProxy::viewIsBecomingVisible()
{
+ m_process->markProcessAsRecentlyUsed();
#if ENABLE(MEDIA_STREAM)
if (m_userMediaPermissionRequestManager)
m_userMediaPermissionRequestManager->viewIsBecomingVisible();
@@ -7739,6 +7749,7 @@
case ProcessTerminationReason::RequestedByGPUProcess:
case ProcessTerminationReason::Crash:
return true;
+ case ProcessTerminationReason::ExceededProcessCountLimit:
case ProcessTerminationReason::NavigationSwap:
case ProcessTerminationReason::RequestedByClient:
break;
Modified: trunk/Source/WebKit/UIProcess/WebProcessPool.cpp (282217 => 282218)
--- trunk/Source/WebKit/UIProcess/WebProcessPool.cpp 2021-09-09 16:08:51 UTC (rev 282217)
+++ trunk/Source/WebKit/UIProcess/WebProcessPool.cpp 2021-09-09 16:40:46 UTC (rev 282218)
@@ -866,6 +866,9 @@
if (m_prewarmedProcess)
return;
+ if (WebProcessProxy::hasReachedProcessCountLimit())
+ return;
+
WEBPROCESSPOOL_RELEASE_LOG(PerformanceLogging, "prewarmProcess: Prewarming a WebProcess for performance");
createNewWebProcess(nullptr, WebProcessProxy::IsPrewarmed::Yes);
}
Modified: trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp (282217 => 282218)
--- trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp 2021-09-09 16:08:51 UTC (rev 282217)
+++ trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp 2021-09-09 16:40:46 UTC (rev 282218)
@@ -108,6 +108,25 @@
namespace WebKit {
using namespace WebCore;
+static unsigned s_maxProcessCount { 400 };
+
+static ListHashSet<WebProcessProxy*>& liveProcessesLRU()
+{
+ ASSERT(RunLoop::isMain());
+ static NeverDestroyed<ListHashSet<WebProcessProxy*>> processes;
+ return processes;
+}
+
+void WebProcessProxy::setProcessCountLimit(unsigned limit)
+{
+ s_maxProcessCount = limit;
+}
+
+bool WebProcessProxy::hasReachedProcessCountLimit()
+{
+ return liveProcessesLRU().size() >= s_maxProcessCount;
+}
+
static bool isMainThreadOrCheckDisabled()
{
#if PLATFORM(IOS_FAMILY)
@@ -150,8 +169,17 @@
Ref<WebProcessProxy> WebProcessProxy::create(WebProcessPool& processPool, WebsiteDataStore* websiteDataStore, IsPrewarmed isPrewarmed, CrossOriginMode crossOriginMode, ShouldLaunchProcess shouldLaunchProcess)
{
auto proxy = adoptRef(*new WebProcessProxy(processPool, websiteDataStore, isPrewarmed, crossOriginMode));
- if (shouldLaunchProcess == ShouldLaunchProcess::Yes)
+ if (shouldLaunchProcess == ShouldLaunchProcess::Yes) {
+ if (liveProcessesLRU().size() >= s_maxProcessCount) {
+ for (auto& processPool : WebProcessPool::allProcessPools())
+ processPool->webProcessCache().clear();
+ if (liveProcessesLRU().size() >= s_maxProcessCount)
+ liveProcessesLRU().first()->requestTermination(ProcessTerminationReason::ExceededProcessCountLimit);
+ }
+ ASSERT(liveProcessesLRU().size() < s_maxProcessCount);
+ liveProcessesLRU().add(proxy.ptr());
proxy->connect();
+ }
return proxy;
}
@@ -237,6 +265,8 @@
ASSERT(m_pageURLRetainCountMap.isEmpty());
WEBPROCESSPROXY_RELEASE_LOG(Process, "destructor:");
+ liveProcessesLRU().remove(this);
+
for (auto identifier : m_speechRecognitionServerMap.keys())
removeMessageReceiver(Messages::SpeechRecognitionServer::messageReceiverName(), identifier);
@@ -337,6 +367,7 @@
ASSERT(!m_isInProcessCache);
ASSERT(!m_provisionalPages.contains(&provisionalPage));
+ markProcessAsRecentlyUsed();
m_provisionalPages.add(&provisionalPage);
updateRegistrationWithDataStore();
}
@@ -558,6 +589,7 @@
m_processPool->pageBeginUsingWebsiteDataStore(webPage.identifier(), webPage.websiteDataStore());
}
+ markProcessAsRecentlyUsed();
m_pageMap.set(webPage.identifier(), &webPage);
globalPageMap().set(webPage.identifier(), &webPage);
@@ -889,6 +921,8 @@
// to be deleted before we can finish our work.
auto protectedThis = makeRef(*this);
+ liveProcessesLRU().remove(this);
+
#if PLATFORM(COCOA) && ENABLE(MEDIA_STREAM)
m_userMediaCaptureManagerProxy->clear();
#endif
@@ -1856,6 +1890,7 @@
void WebProcessProxy::establishServiceWorkerContext(const WebPreferencesStore& store, CompletionHandler<void()>&& completionHandler)
{
WEBPROCESSPROXY_RELEASE_LOG(Loading, "establishServiceWorkerContext: Started");
+ markProcessAsRecentlyUsed();
sendWithAsyncReply(Messages::WebProcess::EstablishWorkerContextConnectionToNetworkProcess { processPool().defaultPageGroup().pageGroupID(), m_serviceWorkerInformation->serviceWorkerPageProxyID, m_serviceWorkerInformation->serviceWorkerPageID, store, *m_registrableDomain, m_serviceWorkerInformation->initializationData }, [this, weakThis = makeWeakPtr(*this), completionHandler = WTFMove(completionHandler)]() mutable {
if (weakThis)
WEBPROCESSPROXY_RELEASE_LOG(Loading, "establishServiceWorkerContext: Finished");
@@ -2006,6 +2041,12 @@
return !m_sleepDisablers.isEmpty();
}
+void WebProcessProxy::markProcessAsRecentlyUsed()
+{
+ if (liveProcessesLRU().contains(this))
+ liveProcessesLRU().appendOrMoveToLast(this);
+}
+
void WebProcessProxy::systemBeep()
{
PAL::systemBeep();
Modified: trunk/Source/WebKit/UIProcess/WebProcessProxy.h (282217 => 282218)
--- trunk/Source/WebKit/UIProcess/WebProcessProxy.h 2021-09-09 16:08:51 UTC (rev 282217)
+++ trunk/Source/WebKit/UIProcess/WebProcessProxy.h 2021-09-09 16:40:46 UTC (rev 282218)
@@ -166,6 +166,9 @@
PAL::SessionID sessionID() const;
+ static bool hasReachedProcessCountLimit();
+ static void setProcessCountLimit(unsigned);
+
static WebProcessProxy* processForIdentifier(WebCore::ProcessIdentifier);
static WebPageProxy* webPage(WebPageProxyIdentifier);
Ref<WebPageProxy> createWebPage(PageClient&, Ref<API::PageConfiguration>&&);
@@ -410,6 +413,8 @@
static bool shouldEnableRemoteInspector();
#endif
+ void markProcessAsRecentlyUsed();
+
#if PLATFORM(MAC)
void platformSuspendProcess();
void platformResumeProcess();
Modified: trunk/Tools/ChangeLog (282217 => 282218)
--- trunk/Tools/ChangeLog 2021-09-09 16:08:51 UTC (rev 282217)
+++ trunk/Tools/ChangeLog 2021-09-09 16:40:46 UTC (rev 282218)
@@ -1,3 +1,16 @@
+2021-09-09 Chris Dumez <[email protected]>
+
+ Implement a WebProcess cap
+ https://bugs.webkit.org/show_bug.cgi?id=230067
+ <rdar://79479244>
+
+ Reviewed by Geoffrey Garen.
+
+ Add API test coverage.
+
+ * TestWebKitAPI/Tests/WebKitCocoa/WebContentProcessDidTerminate.mm:
+ (TEST):
+
2021-09-09 Aakash Jain <[email protected]>
Run check-github-mirror-integrity more frequently
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebContentProcessDidTerminate.mm (282217 => 282218)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebContentProcessDidTerminate.mm 2021-09-09 16:08:51 UTC (rev 282217)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebContentProcessDidTerminate.mm 2021-09-09 16:40:46 UTC (rev 282218)
@@ -376,3 +376,67 @@
kill([webView _webProcessIdentifier], 9);
TestWebKitAPI::Util::run(&done);
}
+
+TEST(WKNavigation, WebProcessLimit)
+{
+ constexpr unsigned maxProcessCount = 10;
+ [WKProcessPool _setWebProcessCountLimit:maxProcessCount];
+
+ auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]);
+ [navigationDelegate setDidFinishNavigation:^(WKWebView *, WKNavigation *) {
+ finishedLoad = true;
+ }];
+ auto createWebView = [&] {
+ auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+ auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) configuration:configuration.get()]);
+ [webView setNavigationDelegate:navigationDelegate.get()];
+ finishedLoad = false;
+ [webView loadTestPageNamed:@"simple"];
+ TestWebKitAPI::Util::run(&finishedLoad);
+ return webView;
+ };
+
+ [navigationDelegate setWebContentProcessDidTerminate:^(WKWebView *) {
+ didCrash = true;
+ }];
+
+ Vector<RetainPtr<WKWebView>> views;
+ for (unsigned i = 0; i < maxProcessCount; ++i)
+ views.append(createWebView());
+ EXPECT_FALSE(didCrash);
+ for (auto& view : views)
+ EXPECT_NE([view _webProcessIdentifier], 0);
+
+ // We have now reached the WebProcess cap, let's try and launch a new one.
+ __block unsigned crashCount = 0;
+ [navigationDelegate setWebContentProcessDidTerminate:^(WKWebView * view) {
+ EXPECT_EQ(views[0], view);
+ ++crashCount;
+ }];
+ views.append(createWebView());
+
+ EXPECT_EQ(crashCount, 1U);
+ for (unsigned i = 0; i < views.size(); ++i) {
+ if (!i)
+ EXPECT_EQ([views[i] _webProcessIdentifier], 0);
+ else
+ EXPECT_NE([views[i] _webProcessIdentifier], 0);
+ }
+
+ crashCount = 0;
+ [navigationDelegate setWebContentProcessDidTerminate:^(WKWebView * view) {
+ EXPECT_EQ(views[1], view);
+ ++crashCount;
+ }];
+ views.append(createWebView());
+
+ EXPECT_EQ(crashCount, 1U);
+ for (unsigned i = 0; i < views.size(); ++i) {
+ if (i < 2)
+ EXPECT_EQ([views[i] _webProcessIdentifier], 0);
+ else
+ EXPECT_NE([views[i] _webProcessIdentifier], 0);
+ }
+
+ [WKProcessPool _setWebProcessCountLimit:400];
+}