Title: [251577] trunk/Source/WebKit
Revision
251577
Author
cdu...@apple.com
Date
2019-10-24 18:07:08 -0700 (Thu, 24 Oct 2019)

Log Message

Simplify ProcessThrottler implementation
https://bugs.webkit.org/show_bug.cgi?id=203370

Reviewed by Alex Christensen.

Simplify ProcessThrottler implementation by:
- Getting rid of CancelPrepareToSuspend IPC. Instead a regular ProcessDidResume IPC is sent to
  the child process.
- Getting rid of the ProcessWillSuspendImminently IPC and send a regular ProcessDidResume IPC
  with a 'isSuspensionImminent' flag instead.
- Whether the suspension is imminent or not, the child process now always responds with
  a ProcessReadyToSuspend IPC. This simplifies our logic as the idea is that treating imminent
  and non-imminent suspension should share as much of the same logic as possible.
- All PrepareToSuspend IPCs now have an associated identifier and the child process sends back
  this identifier when responding with a ProcessReadyToSuspend IPC. This allows the
  ProcessThrottler to easily ignore outdated requests to suspend, without requiring the
  m_suspendMessageCount data member we had.

This patch also adds more logging to ProcessThrottler and the suspension logic in the child
processes. All ProcessThrottler logging now also shows the child process's PID for clarity.

* NetworkProcess/NetworkProcess.cpp:
(WebKit::NetworkProcess::processWillSuspendImminentlyForTestingSync):
(WebKit::NetworkProcess::prepareToSuspend):
* NetworkProcess/NetworkProcess.h:
* NetworkProcess/NetworkProcess.messages.in:
* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _processWillSuspendImminentlyForTesting]):
* UIProcess/Network/NetworkProcessProxy.cpp:
(WebKit::NetworkProcessProxy::sendPrepareToSuspend):
(WebKit::NetworkProcessProxy::processReadyToSuspend):
* UIProcess/Network/NetworkProcessProxy.h:
* UIProcess/Network/NetworkProcessProxy.messages.in:
* UIProcess/ProcessThrottler.cpp:
(WebKit::generatePrepareToSuspendRequestID):
(WebKit::ProcessThrottler::ProcessThrottler):
(WebKit::m_backgroundCounter):
(WebKit::ProcessThrottler::expectedAssertionState):
(WebKit::ProcessThrottler::updateAssertionStateNow):
(WebKit::ProcessThrottler::setAssertionState):
(WebKit::ProcessThrottler::updateAssertionIfNeeded):
(WebKit::ProcessThrottler::didConnectToProcess):
(WebKit::ProcessThrottler::prepareToSuspendTimeoutTimerFired):
(WebKit::ProcessThrottler::processReadyToSuspend):
(WebKit::ProcessThrottler::clearPendingRequestToSuspend):
(WebKit::ProcessThrottler::sendPrepareToSuspendIPC):
(WebKit::ProcessThrottler::uiAssertionWillExpireImminently):
* UIProcess/ProcessThrottler.h:
* UIProcess/ProcessThrottlerClient.h:
* UIProcess/WebProcessProxy.cpp:
(WebKit::WebProcessProxy::sendPrepareToSuspend):
(WebKit::WebProcessProxy::processReadyToSuspend):
* UIProcess/WebProcessProxy.h:
* UIProcess/WebProcessProxy.messages.in:
* WebProcess/WebProcess.cpp:
(WebKit::WebProcess::prepareToSuspend):
* WebProcess/WebProcess.h:
* WebProcess/WebProcess.messages.in:

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (251576 => 251577)


--- trunk/Source/WebKit/ChangeLog	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/ChangeLog	2019-10-25 01:07:08 UTC (rev 251577)
@@ -1,3 +1,64 @@
+2019-10-24  Chris Dumez  <cdu...@apple.com>
+
+        Simplify ProcessThrottler implementation
+        https://bugs.webkit.org/show_bug.cgi?id=203370
+
+        Reviewed by Alex Christensen.
+
+        Simplify ProcessThrottler implementation by:
+        - Getting rid of CancelPrepareToSuspend IPC. Instead a regular ProcessDidResume IPC is sent to
+          the child process.
+        - Getting rid of the ProcessWillSuspendImminently IPC and send a regular ProcessDidResume IPC
+          with a 'isSuspensionImminent' flag instead.
+        - Whether the suspension is imminent or not, the child process now always responds with
+          a ProcessReadyToSuspend IPC. This simplifies our logic as the idea is that treating imminent
+          and non-imminent suspension should share as much of the same logic as possible.
+        - All PrepareToSuspend IPCs now have an associated identifier and the child process sends back
+          this identifier when responding with a ProcessReadyToSuspend IPC. This allows the
+          ProcessThrottler to easily ignore outdated requests to suspend, without requiring the
+          m_suspendMessageCount data member we had.
+
+        This patch also adds more logging to ProcessThrottler and the suspension logic in the child
+        processes. All ProcessThrottler logging now also shows the child process's PID for clarity.
+
+        * NetworkProcess/NetworkProcess.cpp:
+        (WebKit::NetworkProcess::processWillSuspendImminentlyForTestingSync):
+        (WebKit::NetworkProcess::prepareToSuspend):
+        * NetworkProcess/NetworkProcess.h:
+        * NetworkProcess/NetworkProcess.messages.in:
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _processWillSuspendImminentlyForTesting]):
+        * UIProcess/Network/NetworkProcessProxy.cpp:
+        (WebKit::NetworkProcessProxy::sendPrepareToSuspend):
+        (WebKit::NetworkProcessProxy::processReadyToSuspend):
+        * UIProcess/Network/NetworkProcessProxy.h:
+        * UIProcess/Network/NetworkProcessProxy.messages.in:
+        * UIProcess/ProcessThrottler.cpp:
+        (WebKit::generatePrepareToSuspendRequestID):
+        (WebKit::ProcessThrottler::ProcessThrottler):
+        (WebKit::m_backgroundCounter):
+        (WebKit::ProcessThrottler::expectedAssertionState):
+        (WebKit::ProcessThrottler::updateAssertionStateNow):
+        (WebKit::ProcessThrottler::setAssertionState):
+        (WebKit::ProcessThrottler::updateAssertionIfNeeded):
+        (WebKit::ProcessThrottler::didConnectToProcess):
+        (WebKit::ProcessThrottler::prepareToSuspendTimeoutTimerFired):
+        (WebKit::ProcessThrottler::processReadyToSuspend):
+        (WebKit::ProcessThrottler::clearPendingRequestToSuspend):
+        (WebKit::ProcessThrottler::sendPrepareToSuspendIPC):
+        (WebKit::ProcessThrottler::uiAssertionWillExpireImminently):
+        * UIProcess/ProcessThrottler.h:
+        * UIProcess/ProcessThrottlerClient.h:
+        * UIProcess/WebProcessProxy.cpp:
+        (WebKit::WebProcessProxy::sendPrepareToSuspend):
+        (WebKit::WebProcessProxy::processReadyToSuspend):
+        * UIProcess/WebProcessProxy.h:
+        * UIProcess/WebProcessProxy.messages.in:
+        * WebProcess/WebProcess.cpp:
+        (WebKit::WebProcess::prepareToSuspend):
+        * WebProcess/WebProcess.h:
+        * WebProcess/WebProcess.messages.in:
+
 2019-10-23  Ryosuke Niwa  <rn...@webkit.org>
 
         Add a mechanism to find and manipulate text by paragraphs

Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp (251576 => 251577)


--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp	2019-10-25 01:07:08 UTC (rev 251577)
@@ -2075,8 +2075,21 @@
     platformProcessDidTransitionToBackground();
 }
 
-void NetworkProcess::actualPrepareToSuspend(ShouldAcknowledgeWhenReadyToSuspend shouldAcknowledgeWhenReadyToSuspend)
+void NetworkProcess::processWillSuspendImminentlyForTestingSync(CompletionHandler<void()>&& completionHandler)
 {
+    prepareToSuspend(0, true);
+    completionHandler();
+}
+
+void NetworkProcess::prepareToSuspend(uint64_t requestToSuspendID, bool isSuspensionImminent)
+{
+    RELEASE_LOG(ProcessSuspension, "%p - NetworkProcess::prepareToSuspend(%" PRIu64 "), isSuspensionImminent: %d", this, requestToSuspendID, isSuspensionImminent);
+
+#if PLATFORM(IOS_FAMILY) && ENABLE(INDEXED_DATABASE)
+    for (auto& server : m_idbServers.values())
+        server->tryStop(isSuspensionImminent ? IDBServer::ShouldForceStop::Yes : IDBServer::ShouldForceStop::No);
+#endif
+
 #if PLATFORM(IOS_FAMILY)
     m_webSQLiteDatabaseTracker.setIsSuspended(true);
 #endif
@@ -2084,11 +2097,11 @@
     lowMemoryHandler(Critical::Yes);
 
     RefPtr<CallbackAggregator> callbackAggregator;
-    if (shouldAcknowledgeWhenReadyToSuspend == ShouldAcknowledgeWhenReadyToSuspend::Yes) {
-        callbackAggregator = CallbackAggregator::create([this] {
-            RELEASE_LOG(ProcessSuspension, "%p - NetworkProcess::notifyProcessReadyToSuspend() Sending ProcessReadyToSuspend IPC message", this);
+    if (requestToSuspendID) {
+        callbackAggregator = CallbackAggregator::create([this, requestToSuspendID] {
+            RELEASE_LOG(ProcessSuspension, "%p - NetworkProcess::notifyProcessReadyToSuspend(%" PRIu64 ") Sending ProcessReadyToSuspend IPC message", this, requestToSuspendID);
             if (parentProcessConnection())
-                parentProcessConnection()->send(Messages::NetworkProcessProxy::ProcessReadyToSuspend(), 0);
+                parentProcessConnection()->send(Messages::NetworkProcessProxy::ProcessReadyToSuspend(requestToSuspendID), 0);
         });
     }
 
@@ -2108,44 +2121,6 @@
     m_storageManagerSet->suspend([callbackAggregator] { });
 }
 
-void NetworkProcess::processWillSuspendImminently()
-{
-    RELEASE_LOG(ProcessSuspension, "%p - NetworkProcess::processWillSuspendImminently() BEGIN", this);
-#if PLATFORM(IOS_FAMILY) && ENABLE(INDEXED_DATABASE)
-    for (auto& server : m_idbServers.values())
-        server->tryStop(IDBServer::ShouldForceStop::Yes);
-#endif
-    actualPrepareToSuspend(ShouldAcknowledgeWhenReadyToSuspend::No);
-    RELEASE_LOG(ProcessSuspension, "%p - NetworkProcess::processWillSuspendImminently() END", this);
-}
-
-void NetworkProcess::processWillSuspendImminentlyForTestingSync(CompletionHandler<void()>&& completionHandler)
-{
-    processWillSuspendImminently();
-    completionHandler();
-}
-
-void NetworkProcess::prepareToSuspend()
-{
-    RELEASE_LOG(ProcessSuspension, "%p - NetworkProcess::prepareToSuspend()", this);
-
-#if PLATFORM(IOS_FAMILY) && ENABLE(INDEXED_DATABASE)
-    for (auto& server : m_idbServers.values())
-        server->tryStop(IDBServer::ShouldForceStop::No);
-#endif
-    actualPrepareToSuspend(ShouldAcknowledgeWhenReadyToSuspend::Yes);
-}
-
-void NetworkProcess::cancelPrepareToSuspend()
-{
-    // Although it is tempting to send a NetworkProcessProxy::DidCancelProcessSuspension message from here
-    // we do not because prepareToSuspend() already replied with a NetworkProcessProxy::ProcessReadyToSuspend
-    // message. And NetworkProcessProxy expects to receive either a NetworkProcessProxy::ProcessReadyToSuspend-
-    // or NetworkProcessProxy::DidCancelProcessSuspension- message, but not both.
-    RELEASE_LOG(ProcessSuspension, "%p - NetworkProcess::cancelPrepareToSuspend()", this);
-    resume();
-}
-
 void NetworkProcess::applicationDidEnterBackground()
 {
     m_downloadManager.applicationDidEnterBackground();

Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.h (251576 => 251577)


--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.h	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.h	2019-10-25 01:07:08 UTC (rev 251577)
@@ -174,10 +174,8 @@
     void ensureSession(const PAL::SessionID&, bool shouldUseTestingNetworkSession, const String& identifier);
 #endif
 
-    void processWillSuspendImminently();
     void processWillSuspendImminentlyForTestingSync(CompletionHandler<void()>&&);
-    void prepareToSuspend();
-    void cancelPrepareToSuspend();
+    void prepareToSuspend(uint64_t requestToSuspendID, bool isSuspensionImminent);
     void processDidResume();
     void resume();
 
@@ -357,8 +355,6 @@
     void platformProcessDidTransitionToForeground();
     void platformProcessDidTransitionToBackground();
 
-    enum class ShouldAcknowledgeWhenReadyToSuspend { No, Yes };
-    void actualPrepareToSuspend(ShouldAcknowledgeWhenReadyToSuspend);
     void platformPrepareToSuspend(CompletionHandler<void()>&&);
     void platformProcessDidResume();
 

Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.messages.in (251576 => 251577)


--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.messages.in	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.messages.in	2019-10-25 01:07:08 UTC (rev 251577)
@@ -74,10 +74,8 @@
     ProcessDidTransitionToBackground()
     ProcessDidTransitionToForeground()
 
-    ProcessWillSuspendImminently()
     ProcessWillSuspendImminentlyForTestingSync() -> () Synchronous
-    PrepareToSuspend()
-    CancelPrepareToSuspend()
+    PrepareToSuspend(uint64_t requestToSuspendID, bool isSuspensionImminent)
     ProcessDidResume()
 
 #if ENABLE(RESOURCE_LOAD_STATISTICS)

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm (251576 => 251577)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm	2019-10-25 01:07:08 UTC (rev 251577)
@@ -7611,7 +7611,7 @@
 - (void)_processWillSuspendImminentlyForTesting
 {
     if (_page)
-        _page->process().sendProcessWillSuspendImminently();
+        _page->process().sendPrepareToSuspend(0, WebKit::IsSuspensionImminent::Yes);
 }
 
 - (void)_processDidResumeForTesting

Modified: trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp (251576 => 251577)


--- trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp	2019-10-25 01:07:08 UTC (rev 251577)
@@ -1068,12 +1068,6 @@
 }
 #endif // ENABLE(RESOURCE_LOAD_STATISTICS)
 
-void NetworkProcessProxy::sendProcessWillSuspendImminently()
-{
-    if (canSendMessage())
-        send(Messages::NetworkProcess::ProcessWillSuspendImminently(), 0);
-}
-
 void NetworkProcessProxy::sendProcessWillSuspendImminentlyForTesting()
 {
     if (canSendMessage())
@@ -1080,18 +1074,12 @@
         sendSync(Messages::NetworkProcess::ProcessWillSuspendImminentlyForTestingSync(), Messages::NetworkProcess::ProcessWillSuspendImminentlyForTestingSync::Reply(), 0);
 }
     
-void NetworkProcessProxy::sendPrepareToSuspend()
+void NetworkProcessProxy::sendPrepareToSuspend(uint64_t requestToSuspendID, IsSuspensionImminent isSuspensionImminent)
 {
     if (canSendMessage())
-        send(Messages::NetworkProcess::PrepareToSuspend(), 0);
+        send(Messages::NetworkProcess::PrepareToSuspend(requestToSuspendID, isSuspensionImminent == IsSuspensionImminent::Yes), 0);
 }
 
-void NetworkProcessProxy::sendCancelPrepareToSuspend()
-{
-    if (canSendMessage())
-        send(Messages::NetworkProcess::CancelPrepareToSuspend(), 0);
-}
-
 void NetworkProcessProxy::sendProcessDidResume()
 {
     if (canSendMessage())
@@ -1098,9 +1086,9 @@
         send(Messages::NetworkProcess::ProcessDidResume(), 0);
 }
 
-void NetworkProcessProxy::processReadyToSuspend()
+void NetworkProcessProxy::processReadyToSuspend(uint64_t requestToSuspendID)
 {
-    m_throttler.processReadyToSuspend();
+    m_throttler.processReadyToSuspend(requestToSuspendID);
 }
 
 void NetworkProcessProxy::didSetAssertionState(AssertionState)

Modified: trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h (251576 => 251577)


--- trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h	2019-10-25 01:07:08 UTC (rev 251577)
@@ -160,7 +160,7 @@
     void setShouldBlockThirdPartyCookiesForTesting(PAL::SessionID, bool, CompletionHandler<void()>&&);
 #endif
 
-    void processReadyToSuspend();
+    void processReadyToSuspend(uint64_t requestToSuspendID);
     
     void sendProcessDidTransitionToForeground();
     void sendProcessDidTransitionToBackground();
@@ -191,7 +191,6 @@
 #endif
 
     // ProcessThrottlerClient
-    void sendProcessWillSuspendImminently() final;
     void sendProcessDidResume() final;
     
     void sendProcessWillSuspendImminentlyForTesting();
@@ -209,8 +208,7 @@
     void clearCallbackStates();
 
     // ProcessThrottlerClient
-    void sendPrepareToSuspend() final;
-    void sendCancelPrepareToSuspend() final;
+    void sendPrepareToSuspend(uint64_t requestToSuspendID, IsSuspensionImminent) final;
     void didSetAssertionState(AssertionState) final;
 
     // IPC::Connection::Client

Modified: trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.messages.in (251576 => 251577)


--- trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.messages.in	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.messages.in	2019-10-25 01:07:08 UTC (rev 251577)
@@ -31,7 +31,7 @@
 
     TestProcessIncomingSyncMessagesWhenWaitingForSyncReply(WebKit::WebPageProxyIdentifier pageID) -> (bool handled) Synchronous
 
-    ProcessReadyToSuspend()
+    ProcessReadyToSuspend(uint64_t pendingRequestToSuspendID)
     SetIsHoldingLockedFiles(bool isHoldingLockedFiles)
 
     # Diagnostic messages logging

Modified: trunk/Source/WebKit/UIProcess/ProcessThrottler.cpp (251576 => 251577)


--- trunk/Source/WebKit/UIProcess/ProcessThrottler.cpp	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/UIProcess/ProcessThrottler.cpp	2019-10-25 01:07:08 UTC (rev 251577)
@@ -31,21 +31,25 @@
 
 namespace WebKit {
     
-static const Seconds processSuspensionTimeout { 30_s };
-    
+static const Seconds processSuspensionTimeout { 20_s };
+
+static uint64_t generatePrepareToSuspendRequestID()
+{
+    static uint64_t prepareToSuspendRequestID = 0;
+    return ++prepareToSuspendRequestID;
+}
+
 ProcessThrottler::ProcessThrottler(ProcessThrottlerClient& process, bool shouldTakeUIBackgroundAssertion)
     : m_process(process)
-    , m_suspendTimer(RunLoop::main(), this, &ProcessThrottler::suspendTimerFired)
-    , m_foregroundCounter([this](RefCounterEvent) { updateAssertion(); })
-    , m_backgroundCounter([this](RefCounterEvent) { updateAssertion(); })
+    , m_prepareToSuspendTimeoutTimer(RunLoop::main(), this, &ProcessThrottler::prepareToSuspendTimeoutTimerFired)
+    , m_foregroundCounter([this](RefCounterEvent) { updateAssertionIfNeeded(); })
+    , m_backgroundCounter([this](RefCounterEvent) { updateAssertionIfNeeded(); })
     , m_shouldTakeUIBackgroundAssertion(shouldTakeUIBackgroundAssertion)
 {
 }
     
-AssertionState ProcessThrottler::assertionState()
+AssertionState ProcessThrottler::expectedAssertionState()
 {
-    ASSERT(!m_suspendTimer.isActive());
-    
     if (m_foregroundCounter.value())
         return AssertionState::Foreground;
     if (m_backgroundCounter.value())
@@ -53,86 +57,112 @@
     return AssertionState::Suspended;
 }
     
-void ProcessThrottler::updateAssertionNow()
+void ProcessThrottler::updateAssertionStateNow()
 {
-    m_suspendTimer.stop();
-    if (m_assertion) {
-        auto newState = assertionState();
-        if (m_assertion->state() == newState)
-            return;
-        RELEASE_LOG(ProcessSuspension, "%p - ProcessThrottler::updateAssertionNow() updating process assertion state to %u (foregroundActivities: %lu, backgroundActivities: %lu)", this, newState, m_foregroundCounter.value(), m_backgroundCounter.value());
-        m_assertion->setState(newState);
-        m_process.didSetAssertionState(newState);
-    }
+    setAssertionState(expectedAssertionState());
 }
+
+void ProcessThrottler::setAssertionState(AssertionState newState)
+{
+    RELEASE_ASSERT(m_assertion);
+    if (m_assertion->state() == newState)
+        return;
+
+    RELEASE_LOG(ProcessSuspension, "[PID: %d] %p - ProcessThrottler::setAssertionState() Updating process assertion state to %u (foregroundActivities: %lu, backgroundActivities: %lu)", m_processIdentifier, this, newState, m_foregroundCounter.value(), m_backgroundCounter.value());
+    m_assertion->setState(newState);
+    m_process.didSetAssertionState(newState);
+}
     
-void ProcessThrottler::updateAssertion()
+void ProcessThrottler::updateAssertionIfNeeded()
 {
-    bool shouldBeRunnable = this->shouldBeRunnable();
-
-    // If the process is currently runnable but will be suspended then first give it a chance to complete what it was doing
-    // and clean up - move it to the background and send it a message to notify. Schedule a timeout so it can't stay running
-    // in the background for too long.
-    if (m_assertion && m_assertion->state() != AssertionState::Suspended && !shouldBeRunnable) {
-        ++m_suspendMessageCount;
-        RELEASE_LOG(ProcessSuspension, "%p - ProcessThrottler::updateAssertion() sending PrepareToSuspend IPC", this);
-        m_process.sendPrepareToSuspend();
-        m_suspendTimer.startOneShot(processSuspensionTimeout);
-        m_assertion->setState(AssertionState::Background);
-        m_process.didSetAssertionState(AssertionState::Background);
+    if (!m_assertion)
         return;
-    }
 
-    if (shouldBeRunnable) {
-        // If we're currently waiting for the Web process to do suspension cleanup, but no longer need to be suspended, tell the Web process to cancel the cleanup.
-        if (m_suspendTimer.isActive())
-            m_process.sendCancelPrepareToSuspend();
-
-        if ((m_assertion && m_assertion->state() == AssertionState::Suspended) || m_uiAssertionExpired) {
+    if (shouldBeRunnable()) {
+        if (m_assertion->state() == AssertionState::Suspended || m_pendingRequestToSuspendID) {
+            if (m_assertion->state() == AssertionState::Suspended)
+                RELEASE_LOG(ProcessSuspension, "[PID: %d] %p - ProcessThrottler::updateAssertionIfNeeded() sending ProcessDidResume IPC because the process was suspended", m_processIdentifier, this);
+            else
+                RELEASE_LOG(ProcessSuspension, "[PID: %d] %p - ProcessThrottler::updateAssertionIfNeeded() sending ProcessDidResume IPC because the WebProcess is still processing request to suspend: %" PRIu64, m_processIdentifier, this, *m_pendingRequestToSuspendID);
             m_process.sendProcessDidResume();
-            m_uiAssertionExpired = false;
+            clearPendingRequestToSuspend();
         }
+    } else {
+        // If the process is currently runnable but will be suspended then first give it a chance to complete what it was doing
+        // and clean up - move it to the background and send it a message to notify. Schedule a timeout so it can't stay running
+        // in the background for too long.
+        if (m_assertion->state() != AssertionState::Suspended) {
+            sendPrepareToSuspendIPC(IsSuspensionImminent::No);
+            m_prepareToSuspendTimeoutTimer.startOneShot(processSuspensionTimeout);
+            return;
+        }
     }
 
-    updateAssertionNow();
+    updateAssertionStateNow();
 }
 
 void ProcessThrottler::didConnectToProcess(ProcessID pid)
 {
-    RELEASE_LOG(ProcessSuspension, "%p - ProcessThrottler::didConnectToProcess(%d)", this, pid);
+    RELEASE_LOG(ProcessSuspension, "[PID: %d] %p - ProcessThrottler::didConnectToProcess()", pid, this);
+    RELEASE_ASSERT(!m_assertion);
 
-    m_suspendTimer.stop();
     if (m_shouldTakeUIBackgroundAssertion)
-        m_assertion = makeUnique<ProcessAndUIAssertion>(pid, "Web content visibility"_s, assertionState());
+        m_assertion = makeUnique<ProcessAndUIAssertion>(pid, "Web content visibility"_s, expectedAssertionState());
     else
-        m_assertion = makeUnique<ProcessAssertion>(pid, "Web content visibility"_s, assertionState());
-    m_process.didSetAssertionState(assertionState());
+        m_assertion = makeUnique<ProcessAssertion>(pid, "Web content visibility"_s, expectedAssertionState());
+
+    m_processIdentifier = pid;
+    m_process.didSetAssertionState(expectedAssertionState());
     m_assertion->setClient(*this);
 }
     
-void ProcessThrottler::suspendTimerFired()
+void ProcessThrottler::prepareToSuspendTimeoutTimerFired()
 {
-    updateAssertionNow();
+    RELEASE_LOG(ProcessSuspension, "[PID: %d] %p - ProcessThrottler::prepareToSuspendTimeoutTimerFired() Updating process assertion to allow suspension", m_processIdentifier, this);
+    RELEASE_ASSERT(m_pendingRequestToSuspendID);
+    updateAssertionStateNow();
 }
     
-void ProcessThrottler::processReadyToSuspend()
+void ProcessThrottler::processReadyToSuspend(uint64_t requestToSuspendID)
 {
-    if (!--m_suspendMessageCount)
-        updateAssertionNow();
-    ASSERT(m_suspendMessageCount >= 0);
+    RELEASE_ASSERT(requestToSuspendID);
+    if (!m_pendingRequestToSuspendID || *m_pendingRequestToSuspendID != requestToSuspendID)
+        return;
+
+    RELEASE_LOG(ProcessSuspension, "[PID: %d] %p - ProcessThrottler::processReadyToSuspend(%" PRIu64 ") Updating process assertion to allow suspension", m_processIdentifier, this, requestToSuspendID);
+    clearPendingRequestToSuspend();
+
+    if (m_assertion->state() != AssertionState::Suspended)
+        updateAssertionStateNow();
 }
 
-void ProcessThrottler::didCancelProcessSuspension()
+void ProcessThrottler::clearPendingRequestToSuspend()
 {
-    if (!--m_suspendMessageCount)
-        updateAssertionNow();
-    ASSERT(m_suspendMessageCount >= 0);
+    m_prepareToSuspendTimeoutTimer.stop();
+    m_pendingRequestToSuspendID = WTF::nullopt;
 }
 
+void ProcessThrottler::sendPrepareToSuspendIPC(IsSuspensionImminent isSuspensionImminent)
+{
+    RELEASE_LOG(ProcessSuspension, "[PID: %d] %p - ProcessThrottler::sendPrepareToSuspendIPC() isSuspensionImminent: %d", m_processIdentifier, this, isSuspensionImminent == IsSuspensionImminent::Yes);
+    if (m_pendingRequestToSuspendID) {
+        // Do not send a new PrepareToSuspend IPC for imminent suspension if we've already sent a non-imminent PrepareToSuspend IPC.
+        RELEASE_ASSERT(isSuspensionImminent == IsSuspensionImminent::Yes);
+        RELEASE_LOG(ProcessSuspension, "[PID: %d] %p - ProcessThrottler::sendPrepareToSuspendIPC() Not sending PrepareToSuspend() IPC because there is already one in flight (%" PRIu64 ")", m_processIdentifier, this, *m_pendingRequestToSuspendID);
+    } else {
+        m_pendingRequestToSuspendID = generatePrepareToSuspendRequestID();
+        RELEASE_LOG(ProcessSuspension, "[PID: %d] %p - ProcessThrottler::sendPrepareToSuspendIPC() Sending PrepareToSuspend(%" PRIu64 ", isSuspensionImminent: %d) IPC", m_processIdentifier, this, *m_pendingRequestToSuspendID, isSuspensionImminent == IsSuspensionImminent::Yes);
+        m_process.sendPrepareToSuspend(*m_pendingRequestToSuspendID, isSuspensionImminent);
+    }
+
+    setAssertionState(isSuspensionImminent == IsSuspensionImminent::Yes ? AssertionState::Suspended : AssertionState::Background);
+}
+
 void ProcessThrottler::uiAssertionWillExpireImminently()
 {
-    m_process.sendProcessWillSuspendImminently();
-    m_uiAssertionExpired = true;
+    RELEASE_LOG(ProcessSuspension, "[PID: %d] %p - ProcessThrottler::uiAssertionWillExpireImminently()", m_processIdentifier, this);
+    sendPrepareToSuspendIPC(IsSuspensionImminent::Yes);
+    m_prepareToSuspendTimeoutTimer.stop();
 }
 
-}
+} // namespace WebKit

Modified: trunk/Source/WebKit/UIProcess/ProcessThrottler.h (251576 => 251577)


--- trunk/Source/WebKit/UIProcess/ProcessThrottler.h	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/UIProcess/ProcessThrottler.h	2019-10-25 01:07:08 UTC (rev 251577)
@@ -41,6 +41,8 @@
 typedef RefCounter<ProcessSuppressionDisabledCounterType> ProcessSuppressionDisabledCounter;
 typedef ProcessSuppressionDisabledCounter::Token ProcessSuppressionDisabledToken;
 
+enum class IsSuspensionImminent : bool { No, Yes };
+
 class ProcessThrottlerClient;
 
 class ProcessThrottler : private ProcessAssertion::Client {
@@ -54,31 +56,34 @@
 
     ProcessThrottler(ProcessThrottlerClient&, bool shouldTakeUIBackgroundAssertion);
 
-    inline ForegroundActivityToken foregroundActivityToken() const;
-    inline BackgroundActivityToken backgroundActivityToken() const;
+    ForegroundActivityToken foregroundActivityToken() const;
+    BackgroundActivityToken backgroundActivityToken() const;
     
     void didConnectToProcess(ProcessID);
-    void processReadyToSuspend();
-    void didCancelProcessSuspension();
+    void processReadyToSuspend(uint64_t pendingRequestToSuspendID);
     bool shouldBeRunnable() const { return m_foregroundCounter.value() || m_backgroundCounter.value(); }
 
 private:
-    AssertionState assertionState();
-    void updateAssertion();
-    void updateAssertionNow();
-    void suspendTimerFired();
+    AssertionState expectedAssertionState();
+    void updateAssertionIfNeeded();
+    void updateAssertionStateNow();
+    void setAssertionState(AssertionState);
+    void prepareToSuspendTimeoutTimerFired();
+    void sendPrepareToSuspendIPC(IsSuspensionImminent);
 
     // ProcessAssertionClient
     void uiAssertionWillExpireImminently() override;
 
+    void clearPendingRequestToSuspend();
+
     ProcessThrottlerClient& m_process;
+    ProcessID m_processIdentifier { 0 };
     std::unique_ptr<ProcessAssertion> m_assertion;
-    RunLoop::Timer<ProcessThrottler> m_suspendTimer;
+    RunLoop::Timer<ProcessThrottler> m_prepareToSuspendTimeoutTimer;
     ForegroundActivityCounter m_foregroundCounter;
     BackgroundActivityCounter m_backgroundCounter;
-    int m_suspendMessageCount { 0 };
+    Optional<uint64_t> m_pendingRequestToSuspendID;
     bool m_shouldTakeUIBackgroundAssertion;
-    bool m_uiAssertionExpired { false };
 };
 
 inline ProcessThrottler::ForegroundActivityToken ProcessThrottler::foregroundActivityToken() const

Modified: trunk/Source/WebKit/UIProcess/ProcessThrottlerClient.h (251576 => 251577)


--- trunk/Source/WebKit/UIProcess/ProcessThrottlerClient.h	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/UIProcess/ProcessThrottlerClient.h	2019-10-25 01:07:08 UTC (rev 251577)
@@ -30,13 +30,13 @@
 
 namespace WebKit {
 
+enum class IsSuspensionImminent : bool;
+
 class ProcessThrottlerClient {
 public:
     virtual ~ProcessThrottlerClient() { }
 
-    virtual void sendProcessWillSuspendImminently() = 0;
-    virtual void sendPrepareToSuspend() = 0;
-    virtual void sendCancelPrepareToSuspend() = 0;
+    virtual void sendPrepareToSuspend(uint64_t requestToSuspendID, IsSuspensionImminent) = 0;
     virtual void sendProcessDidResume() = 0;
     virtual void didSetAssertionState(AssertionState) = 0;
 };

Modified: trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp (251576 => 251577)


--- trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp	2019-10-25 01:07:08 UTC (rev 251577)
@@ -1217,24 +1217,12 @@
     return UserData::transform(object, Transformer());
 }
 
-void WebProcessProxy::sendProcessWillSuspendImminently()
+void WebProcessProxy::sendPrepareToSuspend(uint64_t requestToSuspendID, IsSuspensionImminent isSuspensionImminent)
 {
     if (canSendMessage())
-        send(Messages::WebProcess::ProcessWillSuspendImminently(), 0);
+        send(Messages::WebProcess::PrepareToSuspend(requestToSuspendID, isSuspensionImminent == IsSuspensionImminent::Yes), 0);
 }
 
-void WebProcessProxy::sendPrepareToSuspend()
-{
-    if (canSendMessage())
-        send(Messages::WebProcess::PrepareToSuspend(), 0);
-}
-
-void WebProcessProxy::sendCancelPrepareToSuspend()
-{
-    if (canSendMessage())
-        send(Messages::WebProcess::CancelPrepareToSuspend(), 0);
-}
-
 void WebProcessProxy::sendProcessDidResume()
 {
     if (canSendMessage())
@@ -1241,16 +1229,11 @@
         send(Messages::WebProcess::ProcessDidResume(), 0);
 }
 
-void WebProcessProxy::processReadyToSuspend()
+void WebProcessProxy::processReadyToSuspend(uint64_t requestToSuspendID)
 {
-    m_throttler.processReadyToSuspend();
+    m_throttler.processReadyToSuspend(requestToSuspendID);
 }
 
-void WebProcessProxy::didCancelProcessSuspension()
-{
-    m_throttler.didCancelProcessSuspension();
-}
-
 void WebProcessProxy::didSetAssertionState(AssertionState state)
 {
 #if PLATFORM(IOS_FAMILY)

Modified: trunk/Source/WebKit/UIProcess/WebProcessProxy.h (251576 => 251577)


--- trunk/Source/WebKit/UIProcess/WebProcessProxy.h	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/UIProcess/WebProcessProxy.h	2019-10-25 01:07:08 UTC (rev 251577)
@@ -223,8 +223,7 @@
 
     void windowServerConnectionStateChanged();
 
-    void processReadyToSuspend();
-    void didCancelProcessSuspension();
+    void processReadyToSuspend(uint64_t requestToSuspendID);
 
     void setIsHoldingLockedFiles(bool);
 
@@ -299,9 +298,7 @@
     void didStartProvisionalLoadForMainFrame(const URL&);
 
     // ProcessThrottlerClient
-    void sendProcessWillSuspendImminently() override;
-    void sendPrepareToSuspend() override;
-    void sendCancelPrepareToSuspend() override;
+    void sendPrepareToSuspend(uint64_t requestToSuspendID, IsSuspensionImminent) override;
     void sendProcessDidResume() override;
     void didSetAssertionState(AssertionState) override;
 

Modified: trunk/Source/WebKit/UIProcess/WebProcessProxy.messages.in (251576 => 251577)


--- trunk/Source/WebKit/UIProcess/WebProcessProxy.messages.in	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/UIProcess/WebProcessProxy.messages.in	2019-10-25 01:07:08 UTC (rev 251577)
@@ -37,8 +37,7 @@
     GetPluginProcessConnection(uint64_t pluginProcessToken) -> (IPC::Attachment connectionHandle, bool supportsAsynchronousInitialization) Synchronous
 #endif
     GetNetworkProcessConnection() -> (struct WebKit::NetworkProcessConnectionInfo connectionInfo) Synchronous
-    ProcessReadyToSuspend()
-    DidCancelProcessSuspension()
+    ProcessReadyToSuspend(uint64_t requestToSuspendID)
 
     SetIsHoldingLockedFiles(bool isHoldingLockedFiles)
 

Modified: trunk/Source/WebKit/WebProcess/WebProcess.cpp (251576 => 251577)


--- trunk/Source/WebKit/WebProcess/WebProcess.cpp	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/WebProcess/WebProcess.cpp	2019-10-25 01:07:08 UTC (rev 251577)
@@ -1377,16 +1377,17 @@
 }
 #endif
 
-void WebProcess::actualPrepareToSuspend(ShouldAcknowledgeWhenReadyToSuspend shouldAcknowledgeWhenReadyToSuspend)
+void WebProcess::prepareToSuspend(uint64_t requestToSuspendID, bool isSuspensionImminent)
 {
+    RELEASE_LOG(ProcessSuspension, "%p - WebProcess::prepareToSuspend(%" PRIu64 ") isSuspensionImminent: %d", this, requestToSuspendID, isSuspensionImminent);
     SetForScope<bool> suspensionScope(m_isSuspending, true);
     m_processIsSuspended = true;
 
 #if PLATFORM(COCOA)
     if (m_processType == ProcessType::PrewarmedWebContent) {
-        if (shouldAcknowledgeWhenReadyToSuspend == ShouldAcknowledgeWhenReadyToSuspend::Yes) {
-            RELEASE_LOG(ProcessSuspension, "%p - WebProcess::actualPrepareToSuspend() Sending ProcessReadyToSuspend IPC message", this);
-            parentProcessConnection()->send(Messages::WebProcessProxy::ProcessReadyToSuspend(), 0);
+        if (requestToSuspendID) {
+            RELEASE_LOG(ProcessSuspension, "%p - WebProcess::prepareToSuspend() Sending ProcessReadyToSuspend(%" PRIu64 ") IPC message", this, requestToSuspendID);
+            parentProcessConnection()->send(Messages::WebProcessProxy::ProcessReadyToSuspend(requestToSuspendID), 0);
         }
         return;
     }
@@ -1402,7 +1403,7 @@
         MemoryPressureHandler::singleton().releaseMemory(Critical::Yes, Synchronous::Yes);
 
     freezeAllLayerTrees();
-    
+
 #if PLATFORM(COCOA)
     destroyRenderingResources();
 #endif
@@ -1416,68 +1417,19 @@
     updateFreezerStatus();
 #endif
 
-    markAllLayersVolatile([this, shouldAcknowledgeWhenReadyToSuspend](bool success) {
+    markAllLayersVolatile([this, requestToSuspendID](bool success) {
         if (success)
             RELEASE_LOG(ProcessSuspension, "%p - WebProcess::markAllLayersVolatile() Successfuly marked all layers as volatile", this);
         else
             RELEASE_LOG(ProcessSuspension, "%p - WebProcess::markAllLayersVolatile() Failed to mark all layers as volatile", this);
 
-        if (shouldAcknowledgeWhenReadyToSuspend == ShouldAcknowledgeWhenReadyToSuspend::Yes) {
-            RELEASE_LOG(ProcessSuspension, "%p - WebProcess::actualPrepareToSuspend() Sending ProcessReadyToSuspend IPC message", this);
-            parentProcessConnection()->send(Messages::WebProcessProxy::ProcessReadyToSuspend(), 0);
+        if (requestToSuspendID) {
+            RELEASE_LOG(ProcessSuspension, "%p - WebProcess::prepareToSuspend() Sending ProcessReadyToSuspend(%" PRIu64 ") IPC message", this, requestToSuspendID);
+            parentProcessConnection()->send(Messages::WebProcessProxy::ProcessReadyToSuspend(requestToSuspendID), 0);
         }
     });
 }
 
-void WebProcess::processWillSuspendImminently()
-{
-    RELEASE_LOG(ProcessSuspension, "%p - WebProcess::processWillSuspendImminently() BEGIN", this);
-    actualPrepareToSuspend(ShouldAcknowledgeWhenReadyToSuspend::No);
-    RELEASE_LOG(ProcessSuspension, "%p - WebProcess::processWillSuspendImminently() END", this);
-}
-
-void WebProcess::prepareToSuspend()
-{
-    RELEASE_LOG(ProcessSuspension, "%p - WebProcess::prepareToSuspend()", this);
-    actualPrepareToSuspend(ShouldAcknowledgeWhenReadyToSuspend::Yes);
-}
-
-void WebProcess::cancelPrepareToSuspend()
-{
-    RELEASE_LOG(ProcessSuspension, "%p - WebProcess::cancelPrepareToSuspend()", this);
-
-    m_processIsSuspended = false;
-
-#if PLATFORM(COCOA)
-    if (m_processType == ProcessType::PrewarmedWebContent)
-        return;
-#endif
-
-    unfreezeAllLayerTrees();
-
-#if PLATFORM(IOS_FAMILY)
-    m_webSQLiteDatabaseTracker.setIsSuspended(false);
-    SQLiteDatabase::setIsDatabaseOpeningForbidden(false);
-    accessibilityProcessSuspendedNotification(false);
-#endif
-
-#if ENABLE(VIDEO)
-    if (auto* platformMediaSessionManager = PlatformMediaSessionManager::sharedManagerIfExists())
-        platformMediaSessionManager->processDidResume();
-    resumeAllMediaBuffering();
-#endif
-
-    // If we've already finished cleaning up and sent ProcessReadyToSuspend, we
-    // shouldn't send DidCancelProcessSuspension; the UI process strictly expects one or the other.
-    if (!m_pageMarkingLayersAsVolatileCounter)
-        return;
-
-    cancelMarkAllLayersVolatile();
-
-    RELEASE_LOG(ProcessSuspension, "%p - WebProcess::cancelPrepareToSuspend() Sending DidCancelProcessSuspension IPC message", this);
-    parentProcessConnection()->send(Messages::WebProcessProxy::DidCancelProcessSuspension(), 0);
-}
-
 void WebProcess::markAllLayersVolatile(WTF::Function<void(bool)>&& completionHandler)
 {
     RELEASE_LOG(ProcessSuspension, "%p - WebProcess::markAllLayersVolatile()", this);

Modified: trunk/Source/WebKit/WebProcess/WebProcess.h (251576 => 251577)


--- trunk/Source/WebKit/WebProcess/WebProcess.h	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/WebProcess/WebProcess.h	2019-10-25 01:07:08 UTC (rev 251577)
@@ -234,9 +234,7 @@
 
     void setHiddenPageDOMTimerThrottlingIncreaseLimit(int milliseconds);
 
-    void processWillSuspendImminently();
-    void prepareToSuspend();
-    void cancelPrepareToSuspend();
+    void prepareToSuspend(uint64_t requestToSuspendID, bool isSuspensionImminent);
     void processDidResume();
 
     void sendPrewarmInformation(const URL&);
@@ -388,9 +386,6 @@
     void setInjectedBundleParameter(const String& key, const IPC::DataReference&);
     void setInjectedBundleParameters(const IPC::DataReference&);
 
-    enum class ShouldAcknowledgeWhenReadyToSuspend { No, Yes };
-    void actualPrepareToSuspend(ShouldAcknowledgeWhenReadyToSuspend);
-
     bool areAllPagesSuspended() const;
 
     void ensureAutomationSessionProxy(const String& sessionIdentifier);

Modified: trunk/Source/WebKit/WebProcess/WebProcess.messages.in (251576 => 251577)


--- trunk/Source/WebKit/WebProcess/WebProcess.messages.in	2019-10-25 01:00:46 UTC (rev 251576)
+++ trunk/Source/WebKit/WebProcess/WebProcess.messages.in	2019-10-25 01:07:08 UTC (rev 251577)
@@ -93,9 +93,7 @@
     EnsureAutomationSessionProxy(String sessionIdentifier)
     DestroyAutomationSessionProxy()
 
-    ProcessWillSuspendImminently()
-    PrepareToSuspend()
-    CancelPrepareToSuspend()
+    PrepareToSuspend(uint64_t requestToSuspendID, bool isSuspensionImminent)
     ProcessDidResume()
 
     MainThreadPing()
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to