Keep around a pre-warmed process when doing process swap on navigation
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Apr 2018 02:03:27 +0000 (02:03 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Apr 2018 02:03:27 +0000 (02:03 +0000)
https://bugs.webkit.org/show_bug.cgi?id=184765

Reviewed by Ryosuke Niwa.

This patch makes it so that WebProcessPool prewarms a process when process
swap on navigation is turned on. When we do a process swap on navigation,
we first try to grab a prewarmed process before creating a new one.

We try to be smart about when to create these processes. The initial heuristic
that this patch chooses is when we reach the DidFirstVisuallyNonEmptyLayout
layout milestone. We're going to try to improve on this heuristic in:
https://bugs.webkit.org/show_bug.cgi?id=184899

This is a 40% progression on PLT with process swap on navigation turned on.

* UIProcess/ServiceWorkerProcessProxy.cpp:
(WebKit::ServiceWorkerProcessProxy::ServiceWorkerProcessProxy):
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::notifyProcessPoolToPrewarm):
(WebKit::WebPageProxy::didFirstVisuallyNonEmptyLayoutForFrame):
* UIProcess/WebPageProxy.h:
* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::createNewWebProcess):
(WebKit::WebProcessPool::tryTakePrewarmedProcess):
(WebKit::WebProcessPool::warmInitialProcess):
(WebKit::WebProcessPool::disconnectProcess):
(WebKit::WebProcessPool::createWebPage):
(WebKit::WebProcessPool::didReachGoodTimeToPrewarm):
(WebKit::WebProcessPool::processForNavigation):
* UIProcess/WebProcessPool.h:
* UIProcess/WebProcessProxy.cpp:
(WebKit::WebProcessProxy::create):
(WebKit::WebProcessProxy::WebProcessProxy):
(WebKit::m_isInPrewarmedPool):
(WebKit::m_userMediaCaptureManagerProxy): Deleted.
* UIProcess/WebProcessProxy.h:
(WebKit::WebProcessProxy::isInPrewarmedPool const):
(WebKit::WebProcessProxy::setIsInPrewarmedPool):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@230938 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebKit/ChangeLog
Source/WebKit/UIProcess/ServiceWorkerProcessProxy.cpp
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/WebPageProxy.h
Source/WebKit/UIProcess/WebProcessPool.cpp
Source/WebKit/UIProcess/WebProcessPool.h
Source/WebKit/UIProcess/WebProcessProxy.cpp
Source/WebKit/UIProcess/WebProcessProxy.h

index 584dbfc..f2e0523 100644 (file)
@@ -1,3 +1,45 @@
+2018-04-23  Saam Barati  <sbarati@apple.com>
+
+        Keep around a pre-warmed process when doing process swap on navigation
+        https://bugs.webkit.org/show_bug.cgi?id=184765
+
+        Reviewed by Ryosuke Niwa.
+
+        This patch makes it so that WebProcessPool prewarms a process when process
+        swap on navigation is turned on. When we do a process swap on navigation,
+        we first try to grab a prewarmed process before creating a new one.
+        
+        We try to be smart about when to create these processes. The initial heuristic
+        that this patch chooses is when we reach the DidFirstVisuallyNonEmptyLayout
+        layout milestone. We're going to try to improve on this heuristic in:
+        https://bugs.webkit.org/show_bug.cgi?id=184899
+        
+        This is a 40% progression on PLT with process swap on navigation turned on.
+
+        * UIProcess/ServiceWorkerProcessProxy.cpp:
+        (WebKit::ServiceWorkerProcessProxy::ServiceWorkerProcessProxy):
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::notifyProcessPoolToPrewarm):
+        (WebKit::WebPageProxy::didFirstVisuallyNonEmptyLayoutForFrame):
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::createNewWebProcess):
+        (WebKit::WebProcessPool::tryTakePrewarmedProcess):
+        (WebKit::WebProcessPool::warmInitialProcess):
+        (WebKit::WebProcessPool::disconnectProcess):
+        (WebKit::WebProcessPool::createWebPage):
+        (WebKit::WebProcessPool::didReachGoodTimeToPrewarm):
+        (WebKit::WebProcessPool::processForNavigation):
+        * UIProcess/WebProcessPool.h:
+        * UIProcess/WebProcessProxy.cpp:
+        (WebKit::WebProcessProxy::create):
+        (WebKit::WebProcessProxy::WebProcessProxy):
+        (WebKit::m_isInPrewarmedPool):
+        (WebKit::m_userMediaCaptureManagerProxy): Deleted.
+        * UIProcess/WebProcessProxy.h:
+        (WebKit::WebProcessProxy::isInPrewarmedPool const):
+        (WebKit::WebProcessProxy::setIsInPrewarmedPool):
+
 2018-04-23  Michael Catanzaro  <mcatanzaro@igalia.com>
 
         [WPE][GTK] Remove WlUniquePtr<wl_display> footgun
index b61904c..b70b673 100644 (file)
@@ -50,7 +50,7 @@ Ref<ServiceWorkerProcessProxy> ServiceWorkerProcessProxy::create(WebProcessPool&
 }
 
 ServiceWorkerProcessProxy::ServiceWorkerProcessProxy(WebProcessPool& pool, const SecurityOriginData& securityOrigin, WebsiteDataStore& store)
-    : WebProcessProxy { pool, store }
+    : WebProcessProxy { pool, store, IsInPrewarmedPool::No }
     , m_securityOrigin(securityOrigin)
     , m_serviceWorkerPageID(generatePageID())
 {
index 52b4516..14de5c5 100644 (file)
@@ -506,6 +506,11 @@ bool WebPageProxy::isValid() const
     return m_isValid;
 }
 
+void WebPageProxy::notifyProcessPoolToPrewarm()
+{
+    m_process->processPool().didReachGoodTimeToPrewarm();
+}
+
 void WebPageProxy::setPreferences(WebPreferences& preferences)
 {
     if (&preferences == m_preferences.ptr())
@@ -3845,8 +3850,10 @@ void WebPageProxy::didFirstVisuallyNonEmptyLayoutForFrame(uint64_t frameID, cons
 
     m_loaderClient->didFirstVisuallyNonEmptyLayoutForFrame(*this, *frame, m_process->transformHandlesToObjects(userData.object()).get());
 
-    if (frame->isMainFrame())
+    if (frame->isMainFrame()) {
         m_pageClient.didFirstVisuallyNonEmptyLayoutForMainFrame();
+        notifyProcessPoolToPrewarm();
+    }
 }
 
 void WebPageProxy::didLayoutForCustomContentProvider()
index 19764d0..f12325a 100644 (file)
@@ -1302,6 +1302,8 @@ private:
     WebPageProxy(PageClient&, WebProcessProxy&, uint64_t pageID, Ref<API::PageConfiguration>&&);
     void platformInitialize();
 
+    void notifyProcessPoolToPrewarm();
+
     RefPtr<API::Navigation> goToBackForwardItem(WebBackForwardListItem&, WebCore::FrameLoadType);
 
     void updateActivityState(WebCore::ActivityState::Flags flagsToUpdate = WebCore::ActivityState::AllFlags);
index 21dcc3f..ccc7f59 100644 (file)
@@ -736,12 +736,14 @@ void WebProcessPool::resolvePathsForSandboxExtensions()
     platformResolvePathsForSandboxExtensions();
 }
 
-WebProcessProxy& WebProcessPool::createNewWebProcess(WebsiteDataStore& websiteDataStore)
+WebProcessProxy& WebProcessPool::createNewWebProcess(WebsiteDataStore& websiteDataStore, WebProcessProxy::IsInPrewarmedPool isInPrewarmedPool)
 {
-    auto processProxy = WebProcessProxy::create(*this, websiteDataStore);
+    auto processProxy = WebProcessProxy::create(*this, websiteDataStore, isInPrewarmedPool);
     auto& process = processProxy.get();
     initializeNewWebProcess(process, websiteDataStore);
     m_processes.append(WTFMove(processProxy));
+    if (isInPrewarmedPool == WebProcessProxy::IsInPrewarmedPool::Yes)
+        ++m_prewarmedProcessCount;
 
     if (m_serviceWorkerProcessesTerminationTimer.isActive())
         m_serviceWorkerProcessesTerminationTimer.stop();
@@ -749,6 +751,26 @@ WebProcessProxy& WebProcessPool::createNewWebProcess(WebsiteDataStore& websiteDa
     return process;
 }
 
+RefPtr<WebProcessProxy> WebProcessPool::tryTakePrewarmedProcess(WebsiteDataStore& websiteDataStore)
+{
+    if (!m_prewarmedProcessCount)
+        return nullptr;
+
+    for (const auto& process : m_processes) {
+        if (process->isInPrewarmedPool()) {
+            --m_prewarmedProcessCount;
+            process->setIsInPrewarmedPool(false);
+            if (&process->websiteDataStore() != &websiteDataStore)
+                process->send(Messages::WebProcess::AddWebsiteDataStore(websiteDataStore.parameters()), 0);
+            return process.get();
+        }
+    }
+
+    ASSERT_NOT_REACHED();
+    m_prewarmedProcessCount = 0;
+    return nullptr;
+}
+
 #if PLATFORM(MAC)
 static void displayReconfigurationCallBack(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void *userInfo)
 {
@@ -926,7 +948,7 @@ void WebProcessPool::initializeNewWebProcess(WebProcessProxy& process, WebsiteDa
 
 void WebProcessPool::warmInitialProcess()
 {
-    if (m_haveInitialEmptyProcess) {
+    if (m_prewarmedProcessCount) {
         ASSERT(!m_processes.isEmpty());
         return;
     }
@@ -936,9 +958,7 @@ void WebProcessPool::warmInitialProcess()
 
     if (!m_websiteDataStore)
         m_websiteDataStore = API::WebsiteDataStore::defaultDataStore().ptr();
-    createNewWebProcess(m_websiteDataStore->websiteDataStore());
-
-    m_haveInitialEmptyProcess = true;
+    createNewWebProcess(m_websiteDataStore->websiteDataStore(), WebProcessProxy::IsInPrewarmedPool::Yes);
 }
 
 void WebProcessPool::enableProcessTermination()
@@ -994,8 +1014,8 @@ void WebProcessPool::disconnectProcess(WebProcessProxy* process)
 {
     ASSERT(m_processes.contains(process));
 
-    if (m_haveInitialEmptyProcess && process == m_processes.last())
-        m_haveInitialEmptyProcess = false;
+    if (process->isInPrewarmedPool())
+        --m_prewarmedProcessCount;
 
     // FIXME (Multi-WebProcess): <rdar://problem/12239765> Some of the invalidation calls of the other supplements are still necessary in multi-process mode, but they should only affect data structures pertaining to the process being disconnected.
     // Clearing everything causes assertion failures, so it's less trouble to skip that for now.
@@ -1082,14 +1102,14 @@ Ref<WebPageProxy> WebProcessPool::createWebPage(PageClient& pageClient, Ref<API:
     }
 
     RefPtr<WebProcessProxy> process;
-    if (m_haveInitialEmptyProcess) {
-        process = m_processes.last();
-        m_haveInitialEmptyProcess = false;
-    } else if (pageConfiguration->relatedPage()) {
+    if (pageConfiguration->relatedPage()) {
         // Sharing processes, e.g. when creating the page via window.open().
         process = &pageConfiguration->relatedPage()->process();
-    } else
-        process = &createNewWebProcessRespectingProcessCountLimit(pageConfiguration->websiteDataStore()->websiteDataStore());
+    } else {
+        process = tryTakePrewarmedProcess(pageConfiguration->websiteDataStore()->websiteDataStore());
+        if (!process)
+            process = &createNewWebProcessRespectingProcessCountLimit(pageConfiguration->websiteDataStore()->websiteDataStore());
+    }
 
 #if ENABLE(SERVICE_WORKER)
     ASSERT(!is<ServiceWorkerProcessProxy>(*process));
@@ -1223,6 +1243,17 @@ void WebProcessPool::postMessageToInjectedBundle(const String& messageName, API:
     }
 }
 
+void WebProcessPool::didReachGoodTimeToPrewarm()
+{
+    if (!m_configuration->processSwapsOnNavigation())
+        return;
+    if (!m_websiteDataStore)
+        m_websiteDataStore = API::WebsiteDataStore::defaultDataStore().ptr();
+    static constexpr size_t maxPrewarmCount = 1;
+    while (m_prewarmedProcessCount < maxPrewarmCount)
+        createNewWebProcess(m_websiteDataStore->websiteDataStore(), WebProcessProxy::IsInPrewarmedPool::Yes);
+}
+
 void WebProcessPool::populateVisitedLinks()
 {
     m_historyClient->populateVisitedLinks(*this);
@@ -2014,6 +2045,8 @@ Ref<WebProcessProxy> WebProcessPool::processForNavigation(WebPageProxy& page, co
         return page.process();
 
     action = PolicyAction::Suspend;
+    if (RefPtr<WebProcessProxy> process = tryTakePrewarmedProcess(page.websiteDataStore()))
+        return process.releaseNonNull();
     return createNewWebProcess(page.websiteDataStore());
 }
 
index 52d76c8..db34c80 100644 (file)
@@ -106,8 +106,8 @@ int webProcessLatencyQOS();
 int webProcessThroughputQOS();
 #endif
 
-class WebProcessPool final : public API::ObjectImpl<API::Object::Type::ProcessPool>, private IPC::MessageReceiver
-    {
+class WebProcessPool final : public API::ObjectImpl<API::Object::Type::ProcessPool>, private IPC::MessageReceiver {
+
 public:
     static Ref<WebProcessPool> create(API::ProcessPoolConfiguration&);
 
@@ -449,6 +449,7 @@ public:
     Ref<WebProcessProxy> processForNavigation(WebPageProxy&, const API::Navigation&, WebCore::PolicyAction&);
     void registerSuspendedPageProxy(SuspendedPageProxy&);
     void unregisterSuspendedPageProxy(SuspendedPageProxy&);
+    void didReachGoodTimeToPrewarm();
 
 private:
     void platformInitialize();
@@ -456,7 +457,9 @@ private:
     void platformInitializeWebProcess(WebProcessCreationParameters&);
     void platformInvalidateContext();
 
-    WebProcessProxy& createNewWebProcess(WebsiteDataStore&);
+    RefPtr<WebProcessProxy> tryTakePrewarmedProcess(WebsiteDataStore&);
+
+    WebProcessProxy& createNewWebProcess(WebsiteDataStore&, WebProcessProxy::IsInPrewarmedPool = WebProcessProxy::IsInPrewarmedPool::No);
     void initializeNewWebProcess(WebProcessProxy&, WebsiteDataStore&);
 
     void requestWebContentStatistics(StatisticsRequest*);
@@ -516,7 +519,7 @@ private:
     IPC::MessageReceiverMap m_messageReceiverMap;
 
     Vector<RefPtr<WebProcessProxy>> m_processes;
-    bool m_haveInitialEmptyProcess { false };
+    unsigned m_prewarmedProcessCount { 0 };
 
     WebProcessProxy* m_processWithPageCache { nullptr };
 #if ENABLE(SERVICE_WORKER)
index 0750516..4156d83 100644 (file)
@@ -105,14 +105,14 @@ static WebProcessProxy::WebPageProxyMap& globalPageMap()
     return pageMap;
 }
 
-Ref<WebProcessProxy> WebProcessProxy::create(WebProcessPool& processPool, WebsiteDataStore& websiteDataStore)
+Ref<WebProcessProxy> WebProcessProxy::create(WebProcessPool& processPool, WebsiteDataStore& websiteDataStore, IsInPrewarmedPool isInPrewarmedPool)
 {
-    auto proxy = adoptRef(*new WebProcessProxy(processPool, websiteDataStore));
+    auto proxy = adoptRef(*new WebProcessProxy(processPool, websiteDataStore, isInPrewarmedPool));
     proxy->connect();
     return proxy;
 }
 
-WebProcessProxy::WebProcessProxy(WebProcessPool& processPool, WebsiteDataStore& websiteDataStore)
+WebProcessProxy::WebProcessProxy(WebProcessPool& processPool, WebsiteDataStore& websiteDataStore, IsInPrewarmedPool isInPrewarmedPool)
     : ChildProcessProxy(processPool.alwaysRunsAtBackgroundPriority())
     , m_responsivenessTimer(*this)
     , m_backgroundResponsivenessTimer(*this)
@@ -126,6 +126,7 @@ WebProcessProxy::WebProcessProxy(WebProcessPool& processPool, WebsiteDataStore&
 #if PLATFORM(COCOA) && ENABLE(MEDIA_STREAM)
     , m_userMediaCaptureManagerProxy(std::make_unique<UserMediaCaptureManagerProxy>(*this))
 #endif
+    , m_isInPrewarmedPool(isInPrewarmedPool == IsInPrewarmedPool::Yes)
 {
     auto result = allProcesses().add(coreProcessIdentifier(), this);
     ASSERT_UNUSED(result, result.isNewEntry);
index 645c981..ef16842 100644 (file)
@@ -97,7 +97,12 @@ public:
     typedef HashMap<uint64_t, WebPageProxy*> WebPageProxyMap;
     typedef HashMap<uint64_t, RefPtr<API::UserInitiatedAction>> UserInitiatedActionMap;
 
-    static Ref<WebProcessProxy> create(WebProcessPool&, WebsiteDataStore&);
+    enum class IsInPrewarmedPool {
+        No,
+        Yes
+    };
+
+    static Ref<WebProcessProxy> create(WebProcessPool&, WebsiteDataStore&, IsInPrewarmedPool);
     ~WebProcessProxy();
 
     WebConnection* webConnection() const { return m_webConnection.get(); }
@@ -213,9 +218,12 @@ public:
     void releaseBackgroundActivityTokenForFullscreenInput();
 #endif
 
+    bool isInPrewarmedPool() const { return m_isInPrewarmedPool; }
+    void setIsInPrewarmedPool(bool isInPrewarmedPool) { m_isInPrewarmedPool = isInPrewarmedPool; }
+
 protected:
     static uint64_t generatePageID();
-    WebProcessProxy(WebProcessPool&, WebsiteDataStore&);
+    WebProcessProxy(WebProcessPool&, WebsiteDataStore&, IsInPrewarmedPool);
 
     // ChildProcessProxy
     void getLaunchOptions(ProcessLauncher::LaunchOptions&) override;
@@ -340,6 +348,7 @@ private:
     HashMap<uint64_t, CompletionHandler<void(WebCore::MessagePortChannelProvider::HasActivity)>> m_localPortActivityCompletionHandlers;
 
     bool m_hasCommittedAnyProvisionalLoads { false };
+    bool m_isInPrewarmedPool;
 
 #if ENABLE(EXTRA_ZOOM_MODE)
     ProcessThrottler::BackgroundActivityToken m_backgroundActivityTokenForFullscreenFormControls;