Add UI process watchdog on iOS to ensure WebProcess connections close
authorbarraclough@apple.com <barraclough@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 27 May 2014 00:02:34 +0000 (00:02 +0000)
committerbarraclough@apple.com <barraclough@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 27 May 2014 00:02:34 +0000 (00:02 +0000)
https://bugs.webkit.org/show_bug.cgi?id=133200
<rdar://problem/16997983>

Reviewed by Darin Adler.

When the WebProcessProxy wants to disconnect from a WebContent process it just drops the connection,
and hopes the connection closes. There is a watchdog thread in the ChildProcess to try to ensure this
happens.

On iOS the process may not be runnable at the time, preventing termination. Instead add a watchdog in
the UI process to make the process runnable, and to terminate if it doesn't quit in a timely fashion.

* Platform/IPC/Connection.h:
    - added terminateSoon.
* Platform/IPC/mac/ConnectionMac.mm:
(IPC::ConnectionTerminationWatchdog::ConnectionTerminationWatchdog):
    - take an assertion to make the process runnable, and start a watchdog timer.
(IPC::ConnectionTerminationWatchdog::watchdogTimerFired):
    - if the process hasn't quit by the timer the watchdog fires, kill it.
(IPC::Connection::terminateSoon):
    - create a ConnectionTerminationWatchdog.
* UIProcess/WebProcessProxy.cpp:
(WebKit::WebProcessProxy::removeWebPage):
    - when disconnecting from a process, first tell it to terminateSoon.

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

Source/WebKit2/ChangeLog
Source/WebKit2/Platform/IPC/Connection.h
Source/WebKit2/Platform/IPC/mac/ConnectionMac.mm
Source/WebKit2/UIProcess/WebProcessProxy.cpp

index d4db1e4..10310cb 100644 (file)
@@ -1,3 +1,31 @@
+2014-05-22  Gavin Barraclough  <baraclough@apple.com>
+
+        Add UI process watchdog on iOS to ensure WebProcess connections close
+        https://bugs.webkit.org/show_bug.cgi?id=133200
+        <rdar://problem/16997983>
+
+        Reviewed by Darin Adler.
+
+        When the WebProcessProxy wants to disconnect from a WebContent process it just drops the connection,
+        and hopes the connection closes. There is a watchdog thread in the ChildProcess to try to ensure this
+        happens.
+
+        On iOS the process may not be runnable at the time, preventing termination. Instead add a watchdog in
+        the UI process to make the process runnable, and to terminate if it doesn't quit in a timely fashion.
+
+        * Platform/IPC/Connection.h:
+            - added terminateSoon.
+        * Platform/IPC/mac/ConnectionMac.mm:
+        (IPC::ConnectionTerminationWatchdog::ConnectionTerminationWatchdog):
+            - take an assertion to make the process runnable, and start a watchdog timer.
+        (IPC::ConnectionTerminationWatchdog::watchdogTimerFired):
+            - if the process hasn't quit by the timer the watchdog fires, kill it.
+        (IPC::Connection::terminateSoon):
+            - create a ConnectionTerminationWatchdog.
+        * UIProcess/WebProcessProxy.cpp:
+        (WebKit::WebProcessProxy::removeWebPage):
+            - when disconnecting from a process, first tell it to terminateSoon.
+
 2014-05-26  Sam Weinig  <sam@webkit.org>
 
         [WebKit2] Add better default preferences while keeping backward compatibility for the C-SPI
index 8064e4b..a516c1f 100644 (file)
@@ -183,6 +183,7 @@ public:
 
 #if PLATFORM(COCOA)
     bool kill();
+    void terminateSoon(double intervalInSeconds);
 #endif
 
 private:
index b543b1e..7263afc 100644 (file)
 #include <wtf/RunLoop.h>
 #include <xpc/xpc.h>
 
+#if PLATFORM(IOS)
+#include "ProcessAssertion.h"
+#endif
+
 #if __has_include(<xpc/private.h>)
 #include <xpc/private.h>
 #else
@@ -65,6 +69,41 @@ enum {
     MessageBodyIsOutOfLine = 1 << 0
 };
     
+// ConnectionTerminationWatchdog does two things:
+// 1) It sets a watchdog timer to kill the peered process.
+// 2) On iOS, make the process runnable for the duration of the watchdog
+//    to ensure it has a chance to terminate cleanly.
+class ConnectionTerminationWatchdog {
+public:
+    static void createConnectionTerminationWatchdog(XPCPtr<xpc_connection_t>& xpcConnection, double intervalInSeconds)
+    {
+        new ConnectionTerminationWatchdog(xpcConnection, intervalInSeconds);
+    }
+    
+private:
+    ConnectionTerminationWatchdog(XPCPtr<xpc_connection_t>& xpcConnection, double intervalInSeconds)
+        : m_xpcConnection(xpcConnection)
+        , m_watchdogTimer(RunLoop::main(), this, &ConnectionTerminationWatchdog::watchdogTimerFired)
+#if PLATFORM(IOS)
+        , m_assertion(std::make_unique<WebKit::ProcessAssertion>(xpc_connection_get_pid(m_xpcConnection.get()), WebKit::AssertionState::Background))
+#endif
+    {
+        m_watchdogTimer.startOneShot(intervalInSeconds);
+    }
+    
+    void watchdogTimerFired()
+    {
+        xpc_connection_kill(m_xpcConnection.get(), SIGKILL);
+        delete this;
+    }
+
+    XPCPtr<xpc_connection_t> m_xpcConnection;
+    RunLoop::Timer<ConnectionTerminationWatchdog> m_watchdogTimer;
+#if PLATFORM(IOS)
+    std::unique_ptr<WebKit::ProcessAssertion> m_assertion;
+#endif
+};
+    
 void Connection::platformInvalidate()
 {
     if (!m_isConnected)
@@ -97,7 +136,13 @@ void Connection::platformInvalidate()
 
     m_xpcConnection = nullptr;
 }
-
+    
+void Connection::terminateSoon(double intervalInSeconds)
+{
+    if (m_xpcConnection)
+        ConnectionTerminationWatchdog::createConnectionTerminationWatchdog(m_xpcConnection, intervalInSeconds);
+}
+    
 void Connection::platformInitialize(Identifier identifier)
 {
 #if !PLATFORM(IOS)
index 74ea2c9..b45c527 100644 (file)
@@ -210,6 +210,11 @@ void WebProcessProxy::removeWebPage(uint64_t pageID)
     // We only allow this when using a network process, as otherwise the WebProcess needs to preserve its session state.
     if (m_context->usesNetworkProcess() && canTerminateChildProcess()) {
         abortProcessLaunchIfNeeded();
+#if PLATFORM(IOS)
+        // On iOS deploy a watchdog in the UI process, since the content may be suspended.
+        // 30s should be sufficient for any outstanding activity to complete cleanly.
+        connection()->terminateSoon(30);
+#endif
         disconnect();
     }
 }