[iOS] "Unexpectedly Resumed" process assertion may cause us to get terminated
https://bugs.webkit.org/show_bug.cgi?id=203046
<rdar://problem/56179592>

Reviewed by Geoffrey Garen.

This patch implements the following to avoid getting terminated:
1. Schedule the task to release the assertion on a background thread instead of
   the main thread so that we end up releasing the task even if the main thread
   is somehow hung.
2. Add an invalidation handler to the process assertion which releases the assertion
   upon expiration.

* UIProcess/Cocoa/WebProcessProxyCocoa.mm:
(WebKit::WebProcessProxy::processWasUnexpectedlyUnsuspended):
* WebProcess/WebProcess.cpp:
(WebKit::WebProcess::processDidResume):
* WebProcess/WebProcess.h:
* WebProcess/cocoa/WebProcessCocoa.mm:
(WebKit::WebProcess::processTaskStateDidChange):


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251304 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog
index ca39a38..38915f0 100644
--- a/Source/WebKit/ChangeLog
+++ b/Source/WebKit/ChangeLog
@@ -1,3 +1,26 @@
+2019-10-18  Chris Dumez  <cdumez@apple.com>
+
+        [iOS] "Unexpectedly Resumed" process assertion may cause us to get terminated
+        https://bugs.webkit.org/show_bug.cgi?id=203046
+        <rdar://problem/56179592>
+
+        Reviewed by Geoffrey Garen.
+
+        This patch implements the following to avoid getting terminated:
+        1. Schedule the task to release the assertion on a background thread instead of
+           the main thread so that we end up releasing the task even if the main thread
+           is somehow hung.
+        2. Add an invalidation handler to the process assertion which releases the assertion
+           upon expiration.
+
+        * UIProcess/Cocoa/WebProcessProxyCocoa.mm:
+        (WebKit::WebProcessProxy::processWasUnexpectedlyUnsuspended):
+        * WebProcess/WebProcess.cpp:
+        (WebKit::WebProcess::processDidResume):
+        * WebProcess/WebProcess.h:
+        * WebProcess/cocoa/WebProcessCocoa.mm:
+        (WebKit::WebProcess::processTaskStateDidChange):
+
 2019-10-18  Jer Noble  <jer.noble@apple.com>
 
         Add experimental HDR MediaCapabilities support.
diff --git a/Source/WebKit/UIProcess/Cocoa/WebProcessProxyCocoa.mm b/Source/WebKit/UIProcess/Cocoa/WebProcessProxyCocoa.mm
index bb490df..88aa1ce 100644
--- a/Source/WebKit/UIProcess/Cocoa/WebProcessProxyCocoa.mm
+++ b/Source/WebKit/UIProcess/Cocoa/WebProcessProxyCocoa.mm
@@ -194,7 +194,7 @@
 {
     if (m_throttler.shouldBeRunnable()) {
         // The process becoming unsuspended was not unexpected; it likely was notified of its running state
-        // before receiving a procsessDidResume() message from the UIProcess.
+        // before receiving a processDidResume() message from the UIProcess.
         return;
     }
 
diff --git a/Source/WebKit/WebProcess/WebProcess.cpp b/Source/WebKit/WebProcess/WebProcess.cpp
index 6c32b42..0a1025e 100644
--- a/Source/WebKit/WebProcess/WebProcess.cpp
+++ b/Source/WebKit/WebProcess/WebProcess.cpp
@@ -1538,6 +1538,10 @@
     m_webSQLiteDatabaseTracker.setIsSuspended(false);
     SQLiteDatabase::setIsDatabaseOpeningForbidden(false);
     accessibilityProcessSuspendedNotification(false);
+    {
+        LockHolder holder(m_unexpectedlyResumedUIAssertionLock);
+        m_unexpectedlyResumedUIAssertion = nullptr;
+    }
 #endif
 
 #if ENABLE(VIDEO)
diff --git a/Source/WebKit/WebProcess/WebProcess.h b/Source/WebKit/WebProcess/WebProcess.h
index 8c246af..a68ab63 100644
--- a/Source/WebKit/WebProcess/WebProcess.h
+++ b/Source/WebKit/WebProcess/WebProcess.h
@@ -65,6 +65,7 @@
 
 #if PLATFORM(IOS_FAMILY)
 #include "ProcessTaskStateObserver.h"
+OBJC_CLASS BKSProcessAssertion;
 #endif
 
 #if PLATFORM(WAYLAND) && USE(WPE_RENDERER)
@@ -534,6 +535,8 @@
 #if PLATFORM(IOS_FAMILY)
     WebSQLiteDatabaseTracker m_webSQLiteDatabaseTracker;
     Ref<ProcessTaskStateObserver> m_taskStateObserver;
+    Lock m_unexpectedlyResumedUIAssertionLock;
+    RetainPtr<BKSProcessAssertion> m_unexpectedlyResumedUIAssertion;
 #endif
 
     enum PageMarkingLayersAsVolatileCounterType { };
diff --git a/Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm b/Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm
index 20c0164..5d1a302 100644
--- a/Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm
+++ b/Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm
@@ -311,12 +311,26 @@
     if (!m_processIsSuspended)
         return;
 
+    LockHolder holder(m_unexpectedlyResumedUIAssertionLock);
+    if (m_unexpectedlyResumedUIAssertion)
+        return;
+
     // We were awakened from suspension unexpectedly. Notify the WebProcessProxy, but take a process assertion on our parent PID
     // to ensure that it too is awakened.
+    m_unexpectedlyResumedUIAssertion = adoptNS([[BKSProcessAssertion alloc] initWithPID:parentProcessConnection()->remoteProcessID() flags:BKSProcessAssertionPreventTaskSuspend reason:BKSProcessAssertionReasonFinishTask name:@"Unexpectedly resumed" withHandler:nil]);
 
-    auto uiProcessAssertion = adoptNS([[BKSProcessAssertion alloc] initWithPID:parentProcessConnection()->remoteProcessID() flags:BKSProcessAssertionPreventTaskSuspend reason:BKSProcessAssertionReasonFinishTask name:@"Unexpectedly resumed" withHandler:nil]);
+    auto releaseAssertion = [this] {
+        LockHolder holder(m_unexpectedlyResumedUIAssertionLock);
+        if (!m_unexpectedlyResumedUIAssertion)
+            return;
+
+        [m_unexpectedlyResumedUIAssertion invalidate];
+        m_unexpectedlyResumedUIAssertion = nullptr;
+    };
+
+    m_unexpectedlyResumedUIAssertion.get().invalidationHandler = releaseAssertion;
     parentProcessConnection()->send(Messages::WebProcessProxy::ProcessWasUnexpectedlyUnsuspended(), 0);
-    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), [assertion = WTFMove(uiProcessAssertion)] { [assertion invalidate]; });
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), releaseAssertion);
 }
 #endif