Changeset 282218 in webkit
- Timestamp:
- Sep 9, 2021, 9:40:46 AM (5 years ago)
- Location:
- trunk
- Files:
-
- 12 edited
-
Source/WebKit/ChangeLog (modified) (1 diff)
-
Source/WebKit/Shared/ProcessTerminationReason.h (modified) (1 diff)
-
Source/WebKit/UIProcess/API/C/WKAPICast.h (modified) (1 diff)
-
Source/WebKit/UIProcess/API/Cocoa/WKProcessPool.mm (modified) (1 diff)
-
Source/WebKit/UIProcess/API/Cocoa/WKProcessPoolPrivate.h (modified) (1 diff)
-
Source/WebKit/UIProcess/Cocoa/NavigationState.mm (modified) (1 diff)
-
Source/WebKit/UIProcess/WebPageProxy.cpp (modified) (10 diffs)
-
Source/WebKit/UIProcess/WebProcessPool.cpp (modified) (1 diff)
-
Source/WebKit/UIProcess/WebProcessProxy.cpp (modified) (8 diffs)
-
Source/WebKit/UIProcess/WebProcessProxy.h (modified) (2 diffs)
-
Tools/ChangeLog (modified) (1 diff)
-
Tools/TestWebKitAPI/Tests/WebKitCocoa/WebContentProcessDidTerminate.mm (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebKit/ChangeLog
r282215 r282218 1 2021-09-09 Chris Dumez <cdumez@apple.com> 2 3 Implement a WebProcess cap 4 https://bugs.webkit.org/show_bug.cgi?id=230067 5 <rdar://79479244> 6 7 Reviewed by Geoffrey Garen. 8 9 Implement a WebProcess cap to avoid getting into a state where we run out of resources and crash in various ways. 10 The current limit is 400 and can be changed via an SPI. The limit is per UIProcess (not per process pool). 11 When we try and launch a new WebProcess and the limit has been reached, we terminate the least recently used 12 WebProcess to avoid going over the limit. 13 14 * Shared/ProcessTerminationReason.h: 15 * UIProcess/API/C/WKAPICast.h: 16 (WebKit::toAPI): 17 * UIProcess/API/Cocoa/WKProcessPool.mm: 18 (+[WKProcessPool _setWebProcessCountLimit:]): 19 * UIProcess/API/Cocoa/WKProcessPoolPrivate.h: 20 * UIProcess/Cocoa/NavigationState.mm: 21 (WebKit::wkProcessTerminationReason): 22 * UIProcess/WebPageProxy.cpp: 23 (WebKit::WebPageProxy::loadRequestWithNavigationShared): 24 (WebKit::WebPageProxy::loadFile): 25 (WebKit::WebPageProxy::loadDataWithNavigationShared): 26 (WebKit::WebPageProxy::loadSimulatedRequest): 27 (WebKit::WebPageProxy::loadAlternateHTML): 28 (WebKit::WebPageProxy::loadWebArchiveData): 29 (WebKit::WebPageProxy::reload): 30 (WebKit::WebPageProxy::goToBackForwardItem): 31 (WebKit::shouldReloadAfterProcessTermination): 32 * UIProcess/WebProcessProxy.cpp: 33 (WebKit::WebProcessProxy::setProcessCountLimit): 34 (WebKit::WebProcessProxy::create): 35 (WebKit::WebProcessProxy::~WebProcessProxy): 36 (WebKit::WebProcessProxy::addProvisionalPageProxy): 37 (WebKit::WebProcessProxy::addExistingWebPage): 38 (WebKit::WebProcessProxy::processDidTerminateOrFailedToLaunch): 39 (WebKit::WebProcessProxy::establishServiceWorkerContext): 40 (WebKit::WebProcessProxy::markProcessAsRecentlyUsed): 41 * UIProcess/WebProcessProxy.h: 42 1 43 2021-09-09 Wenson Hsieh <wenson_hsieh@apple.com> 2 44 -
trunk/Source/WebKit/Shared/ProcessTerminationReason.h
r278895 r282218 31 31 ExceededMemoryLimit, 32 32 ExceededCPULimit, 33 ExceededProcessCountLimit, 33 34 RequestedByClient, 34 35 Crash, -
trunk/Source/WebKit/UIProcess/API/C/WKAPICast.h
r274815 r282218 244 244 case ProcessTerminationReason::RequestedByClient: 245 245 return kWKProcessTerminationReasonRequestedByClient; 246 case ProcessTerminationReason::ExceededProcessCountLimit: 246 247 case ProcessTerminationReason::RequestedByNetworkProcess: 247 248 case ProcessTerminationReason::RequestedByGPUProcess: -
trunk/Source/WebKit/UIProcess/API/Cocoa/WKProcessPool.mm
r281965 r282218 566 566 } 567 567 568 + (void)_setWebProcessCountLimit:(unsigned)limit 569 { 570 WebKit::WebProcessProxy::setProcessCountLimit(limit); 571 } 572 568 573 - (void)_garbageCollectJavaScriptObjectsForTesting 569 574 { -
trunk/Source/WebKit/UIProcess/API/Cocoa/WKProcessPoolPrivate.h
r281965 r282218 74 74 + (pid_t)_webAuthnProcessIdentifier WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA)); 75 75 76 + (void)_setWebProcessCountLimit:(unsigned)limit WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA)); 77 76 78 - (void)_warmInitialProcess WK_API_AVAILABLE(macos(10.12), ios(10.0)); 77 79 - (void)_automationCapabilitiesDidChange WK_API_AVAILABLE(macos(10.12), ios(10.0)); -
trunk/Source/WebKit/UIProcess/Cocoa/NavigationState.mm
r278315 r282218 1061 1061 case ProcessTerminationReason::RequestedByClient: 1062 1062 return _WKProcessTerminationReasonRequestedByClient; 1063 case ProcessTerminationReason::ExceededProcessCountLimit: 1063 1064 case ProcessTerminationReason::RequestedByNetworkProcess: 1064 1065 case ProcessTerminationReason::RequestedByGPUProcess: -
trunk/Source/WebKit/UIProcess/WebPageProxy.cpp
r282189 r282218 1414 1414 navigation.setIsLoadedWithNavigationShared(true); 1415 1415 1416 process->markProcessAsRecentlyUsed(); 1417 1416 1418 if (!process->isLaunching() || !url.isLocalFile()) 1417 1419 process->send(Messages::WebPage::LoadRequest(loadParameters), webPageID); … … 1475 1477 addPlatformLoadParameters(m_process, loadParameters); 1476 1478 1479 m_process->markProcessAsRecentlyUsed(); 1477 1480 if (m_process->isLaunching()) 1478 1481 send(Messages::WebPage::LoadRequestWaitingForProcessLaunch(loadParameters, resourceDirectoryURL, m_identifier, checkAssumedReadAccessToResourceURL)); … … 1534 1537 addPlatformLoadParameters(process, loadParameters); 1535 1538 1539 process->markProcessAsRecentlyUsed(); 1536 1540 process->assumeReadAccessToBaseURL(*this, baseURL); 1537 1541 process->send(Messages::WebPage::LoadData(loadParameters), webPageID); … … 1591 1595 addPlatformLoadParameters(m_process, loadParameters); 1592 1596 1597 m_process->markProcessAsRecentlyUsed(); 1593 1598 m_process->assumeReadAccessToBaseURL(*this, baseURL); 1594 1599 m_process->send(Messages::WebPage::LoadSimulatedRequestAndResponse(loadParameters, simulatedResponse), m_webPageID); … … 1634 1639 addPlatformLoadParameters(process(), loadParameters); 1635 1640 1641 m_process->markProcessAsRecentlyUsed(); 1636 1642 m_process->assumeReadAccessToBaseURL(*this, baseURL.string()); 1637 1643 m_process->assumeReadAccessToBaseURL(*this, unreachableURL.string()); … … 1663 1669 addPlatformLoadParameters(process(), loadParameters); 1664 1670 1671 m_process->markProcessAsRecentlyUsed(); 1665 1672 send(Messages::WebPage::LoadData(loadParameters)); 1666 1673 m_process->startResponsivenessTimer(); … … 1738 1745 navigation->setUserContentExtensionsEnabled(false); 1739 1746 1747 m_process->markProcessAsRecentlyUsed(); 1740 1748 send(Messages::WebPage::Reload(navigation->navigationID(), options.toRaw(), sandboxExtensionHandle)); 1741 1749 m_process->startResponsivenessTimer(); … … 1816 1824 m_pageLoadState.setPendingAPIRequest(transaction, { navigation ? navigation->navigationID() : 0, item.url() }); 1817 1825 1826 m_process->markProcessAsRecentlyUsed(); 1818 1827 send(Messages::WebPage::GoToBackForwardItem(navigation ? navigation->navigationID() : 0, item.itemID(), frameLoadType, ShouldTreatAsContinuingLoad::No, std::nullopt, m_lastNavigationWasAppInitiated)); 1819 1828 m_process->startResponsivenessTimer(); … … 5177 5186 void WebPageProxy::viewIsBecomingVisible() 5178 5187 { 5188 m_process->markProcessAsRecentlyUsed(); 5179 5189 #if ENABLE(MEDIA_STREAM) 5180 5190 if (m_userMediaPermissionRequestManager) … … 7740 7750 case ProcessTerminationReason::Crash: 7741 7751 return true; 7752 case ProcessTerminationReason::ExceededProcessCountLimit: 7742 7753 case ProcessTerminationReason::NavigationSwap: 7743 7754 case ProcessTerminationReason::RequestedByClient: -
trunk/Source/WebKit/UIProcess/WebProcessPool.cpp
r282034 r282218 867 867 return; 868 868 869 if (WebProcessProxy::hasReachedProcessCountLimit()) 870 return; 871 869 872 WEBPROCESSPOOL_RELEASE_LOG(PerformanceLogging, "prewarmProcess: Prewarming a WebProcess for performance"); 870 873 createNewWebProcess(nullptr, WebProcessProxy::IsPrewarmed::Yes); -
trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp
r281832 r282218 109 109 using namespace WebCore; 110 110 111 static unsigned s_maxProcessCount { 400 }; 112 113 static ListHashSet<WebProcessProxy*>& liveProcessesLRU() 114 { 115 ASSERT(RunLoop::isMain()); 116 static NeverDestroyed<ListHashSet<WebProcessProxy*>> processes; 117 return processes; 118 } 119 120 void WebProcessProxy::setProcessCountLimit(unsigned limit) 121 { 122 s_maxProcessCount = limit; 123 } 124 125 bool WebProcessProxy::hasReachedProcessCountLimit() 126 { 127 return liveProcessesLRU().size() >= s_maxProcessCount; 128 } 129 111 130 static bool isMainThreadOrCheckDisabled() 112 131 { … … 151 170 { 152 171 auto proxy = adoptRef(*new WebProcessProxy(processPool, websiteDataStore, isPrewarmed, crossOriginMode)); 153 if (shouldLaunchProcess == ShouldLaunchProcess::Yes) 172 if (shouldLaunchProcess == ShouldLaunchProcess::Yes) { 173 if (liveProcessesLRU().size() >= s_maxProcessCount) { 174 for (auto& processPool : WebProcessPool::allProcessPools()) 175 processPool->webProcessCache().clear(); 176 if (liveProcessesLRU().size() >= s_maxProcessCount) 177 liveProcessesLRU().first()->requestTermination(ProcessTerminationReason::ExceededProcessCountLimit); 178 } 179 ASSERT(liveProcessesLRU().size() < s_maxProcessCount); 180 liveProcessesLRU().add(proxy.ptr()); 154 181 proxy->connect(); 182 } 155 183 return proxy; 156 184 } … … 238 266 WEBPROCESSPROXY_RELEASE_LOG(Process, "destructor:"); 239 267 268 liveProcessesLRU().remove(this); 269 240 270 for (auto identifier : m_speechRecognitionServerMap.keys()) 241 271 removeMessageReceiver(Messages::SpeechRecognitionServer::messageReceiverName(), identifier); … … 338 368 ASSERT(!m_isInProcessCache); 339 369 ASSERT(!m_provisionalPages.contains(&provisionalPage)); 370 markProcessAsRecentlyUsed(); 340 371 m_provisionalPages.add(&provisionalPage); 341 372 updateRegistrationWithDataStore(); … … 559 590 } 560 591 592 markProcessAsRecentlyUsed(); 561 593 m_pageMap.set(webPage.identifier(), &webPage); 562 594 globalPageMap().set(webPage.identifier(), &webPage); … … 889 921 // to be deleted before we can finish our work. 890 922 auto protectedThis = makeRef(*this); 923 924 liveProcessesLRU().remove(this); 891 925 892 926 #if PLATFORM(COCOA) && ENABLE(MEDIA_STREAM) … … 1857 1891 { 1858 1892 WEBPROCESSPROXY_RELEASE_LOG(Loading, "establishServiceWorkerContext: Started"); 1893 markProcessAsRecentlyUsed(); 1859 1894 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 { 1860 1895 if (weakThis) … … 2007 2042 } 2008 2043 2044 void WebProcessProxy::markProcessAsRecentlyUsed() 2045 { 2046 if (liveProcessesLRU().contains(this)) 2047 liveProcessesLRU().appendOrMoveToLast(this); 2048 } 2049 2009 2050 void WebProcessProxy::systemBeep() 2010 2051 { -
trunk/Source/WebKit/UIProcess/WebProcessProxy.h
r281832 r282218 167 167 PAL::SessionID sessionID() const; 168 168 169 static bool hasReachedProcessCountLimit(); 170 static void setProcessCountLimit(unsigned); 171 169 172 static WebProcessProxy* processForIdentifier(WebCore::ProcessIdentifier); 170 173 static WebPageProxy* webPage(WebPageProxyIdentifier); … … 411 414 #endif 412 415 416 void markProcessAsRecentlyUsed(); 417 413 418 #if PLATFORM(MAC) 414 419 void platformSuspendProcess(); -
trunk/Tools/ChangeLog
r282216 r282218 1 2021-09-09 Chris Dumez <cdumez@apple.com> 2 3 Implement a WebProcess cap 4 https://bugs.webkit.org/show_bug.cgi?id=230067 5 <rdar://79479244> 6 7 Reviewed by Geoffrey Garen. 8 9 Add API test coverage. 10 11 * TestWebKitAPI/Tests/WebKitCocoa/WebContentProcessDidTerminate.mm: 12 (TEST): 13 1 14 2021-09-09 Aakash Jain <aakash_jain@apple.com> 2 15 -
trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebContentProcessDidTerminate.mm
r273997 r282218 377 377 TestWebKitAPI::Util::run(&done); 378 378 } 379 380 TEST(WKNavigation, WebProcessLimit) 381 { 382 constexpr unsigned maxProcessCount = 10; 383 [WKProcessPool _setWebProcessCountLimit:maxProcessCount]; 384 385 auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]); 386 [navigationDelegate setDidFinishNavigation:^(WKWebView *, WKNavigation *) { 387 finishedLoad = true; 388 }]; 389 auto createWebView = [&] { 390 auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]); 391 auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) configuration:configuration.get()]); 392 [webView setNavigationDelegate:navigationDelegate.get()]; 393 finishedLoad = false; 394 [webView loadTestPageNamed:@"simple"]; 395 TestWebKitAPI::Util::run(&finishedLoad); 396 return webView; 397 }; 398 399 [navigationDelegate setWebContentProcessDidTerminate:^(WKWebView *) { 400 didCrash = true; 401 }]; 402 403 Vector<RetainPtr<WKWebView>> views; 404 for (unsigned i = 0; i < maxProcessCount; ++i) 405 views.append(createWebView()); 406 EXPECT_FALSE(didCrash); 407 for (auto& view : views) 408 EXPECT_NE([view _webProcessIdentifier], 0); 409 410 // We have now reached the WebProcess cap, let's try and launch a new one. 411 __block unsigned crashCount = 0; 412 [navigationDelegate setWebContentProcessDidTerminate:^(WKWebView * view) { 413 EXPECT_EQ(views[0], view); 414 ++crashCount; 415 }]; 416 views.append(createWebView()); 417 418 EXPECT_EQ(crashCount, 1U); 419 for (unsigned i = 0; i < views.size(); ++i) { 420 if (!i) 421 EXPECT_EQ([views[i] _webProcessIdentifier], 0); 422 else 423 EXPECT_NE([views[i] _webProcessIdentifier], 0); 424 } 425 426 crashCount = 0; 427 [navigationDelegate setWebContentProcessDidTerminate:^(WKWebView * view) { 428 EXPECT_EQ(views[1], view); 429 ++crashCount; 430 }]; 431 views.append(createWebView()); 432 433 EXPECT_EQ(crashCount, 1U); 434 for (unsigned i = 0; i < views.size(); ++i) { 435 if (i < 2) 436 EXPECT_EQ([views[i] _webProcessIdentifier], 0); 437 else 438 EXPECT_NE([views[i] _webProcessIdentifier], 0); 439 } 440 441 [WKProcessPool _setWebProcessCountLimit:400]; 442 }
Note:
See TracChangeset
for help on using the changeset viewer.