Make garbage collection of MessagePort objects be asynchronous.
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 21 Jan 2018 07:07:49 +0000 (07:07 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 21 Jan 2018 07:07:49 +0000 (07:07 +0000)
https://bugs.webkit.org/show_bug.cgi?id=181910

Reviewed by Andy Estes.

No new tests (Covered by existing tests, including GC-specific ones).

The basic premise here is as follows:
- You can *always* GC a MessagePort that is closed
- You can *always* GC a MessagePort that has no onmessage handler, as incoming messages cannot
  possibly revive it.
- You can GC a MessagePort, even if it has a message handler, as long as there are no messages
  in flight between it and the remote port, and as long as the remote port is "maybe eligible for GC."

A MessagePort is considered "maybe eligible for GC" once hasPendingActivity is asked once.

A MessagePort loses "maybe eligible for GC" status once it is used for sending or receiving a message.

The changes to MessagePort.cpp implement the above with a tiny little bool-driven state machine.
* dom/MessagePort.cpp:
(WebCore::MessagePort::postMessage):
(WebCore::MessagePort::disentangle):
(WebCore::MessagePort::registerLocalActivity):
(WebCore::MessagePort::start):
(WebCore::MessagePort::close):
(WebCore::MessagePort::contextDestroyed):
(WebCore::MessagePort::dispatchMessages):
(WebCore::MessagePort::hasPendingActivity const):
(WebCore::MessagePort::isLocallyReachable const):
(WebCore::MessagePort::addEventListener):
(WebCore::MessagePort::removeEventListener):
* dom/MessagePort.h:

- Remove the lock and any background-thread code paths
- Add ASSERT(isMainThread())s throughout
* dom/messageports/MessagePortChannel.cpp:
(WebCore::MessagePortChannel::MessagePortChannel):
(WebCore::MessagePortChannel::includesPort):
(WebCore::MessagePortChannel::entanglePortWithProcess):
(WebCore::MessagePortChannel::disentanglePort):
(WebCore::MessagePortChannel::closePort):
(WebCore::MessagePortChannel::postMessageToRemote):
(WebCore::MessagePortChannel::takeAllMessagesForPort):
(WebCore::MessagePortChannel::checkRemotePortForActivity):
(WebCore::MessagePortChannel::hasAnyMessagesPendingOrInFlight const):
* dom/messageports/MessagePortChannel.h:

Add a callback for a MessagePortChannel to go ask the remote MessagePort object about local activity:
* dom/messageports/MessagePortChannelProvider.h:
* dom/messageports/MessagePortChannelProviderImpl.cpp:
(WebCore::MessagePortChannelProviderImpl::checkRemotePortForActivity):
(WebCore::MessagePortChannelProviderImpl::checkProcessLocalPortForActivity):
(WebCore::MessagePortChannelProviderImpl::hasMessagesForPorts_temporarySync): Deleted.
* dom/messageports/MessagePortChannelProviderImpl.h:

- Remove the lock and any background-thread code paths
- Add ASSERT(isMainThread())s throughout
* dom/messageports/MessagePortChannelRegistry.cpp:
(WebCore::MessagePortChannelRegistry::messagePortChannelCreated):
(WebCore::MessagePortChannelRegistry::messagePortChannelDestroyed):
(WebCore::MessagePortChannelRegistry::didEntangleLocalToRemote):
(WebCore::MessagePortChannelRegistry::didDisentangleMessagePort):
(WebCore::MessagePortChannelRegistry::didCloseMessagePort):
(WebCore::MessagePortChannelRegistry::didPostMessageToRemote):
(WebCore::MessagePortChannelRegistry::takeAllMessagesForPort):
(WebCore::MessagePortChannelRegistry::checkRemotePortForActivity):
(WebCore::MessagePortChannelRegistry::existingChannelContainingPort):
(WebCore::MessagePortChannelRegistry::hasMessagesForPorts_temporarySync): Deleted.
* dom/messageports/MessagePortChannelRegistry.h:

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

Source/WebCore/ChangeLog
Source/WebCore/dom/MessagePort.cpp
Source/WebCore/dom/MessagePort.h
Source/WebCore/dom/messageports/MessagePortChannel.cpp
Source/WebCore/dom/messageports/MessagePortChannel.h
Source/WebCore/dom/messageports/MessagePortChannelProvider.h
Source/WebCore/dom/messageports/MessagePortChannelProviderImpl.cpp
Source/WebCore/dom/messageports/MessagePortChannelProviderImpl.h
Source/WebCore/dom/messageports/MessagePortChannelRegistry.cpp
Source/WebCore/dom/messageports/MessagePortChannelRegistry.h

index 9d1d001..bc2ec32 100644 (file)
@@ -1,3 +1,75 @@
+2018-01-20  Brady Eidson  <beidson@apple.com>
+
+        Make garbage collection of MessagePort objects be asynchronous.
+        https://bugs.webkit.org/show_bug.cgi?id=181910
+
+        Reviewed by Andy Estes.
+
+        No new tests (Covered by existing tests, including GC-specific ones).
+
+        The basic premise here is as follows:
+        - You can *always* GC a MessagePort that is closed
+        - You can *always* GC a MessagePort that has no onmessage handler, as incoming messages cannot 
+          possibly revive it.
+        - You can GC a MessagePort, even if it has a message handler, as long as there are no messages 
+          in flight between it and the remote port, and as long as the remote port is "maybe eligible for GC."
+          
+        A MessagePort is considered "maybe eligible for GC" once hasPendingActivity is asked once.
+        
+        A MessagePort loses "maybe eligible for GC" status once it is used for sending or receiving a message.
+        
+        The changes to MessagePort.cpp implement the above with a tiny little bool-driven state machine.
+        * dom/MessagePort.cpp:
+        (WebCore::MessagePort::postMessage):
+        (WebCore::MessagePort::disentangle):
+        (WebCore::MessagePort::registerLocalActivity):
+        (WebCore::MessagePort::start):
+        (WebCore::MessagePort::close):
+        (WebCore::MessagePort::contextDestroyed):
+        (WebCore::MessagePort::dispatchMessages):
+        (WebCore::MessagePort::hasPendingActivity const):
+        (WebCore::MessagePort::isLocallyReachable const):
+        (WebCore::MessagePort::addEventListener):
+        (WebCore::MessagePort::removeEventListener):
+        * dom/MessagePort.h:
+
+        - Remove the lock and any background-thread code paths
+        - Add ASSERT(isMainThread())s throughout
+        * dom/messageports/MessagePortChannel.cpp:
+        (WebCore::MessagePortChannel::MessagePortChannel):
+        (WebCore::MessagePortChannel::includesPort):
+        (WebCore::MessagePortChannel::entanglePortWithProcess):
+        (WebCore::MessagePortChannel::disentanglePort):
+        (WebCore::MessagePortChannel::closePort):
+        (WebCore::MessagePortChannel::postMessageToRemote):
+        (WebCore::MessagePortChannel::takeAllMessagesForPort):
+        (WebCore::MessagePortChannel::checkRemotePortForActivity):
+        (WebCore::MessagePortChannel::hasAnyMessagesPendingOrInFlight const):
+        * dom/messageports/MessagePortChannel.h:
+        
+        Add a callback for a MessagePortChannel to go ask the remote MessagePort object about local activity:
+        * dom/messageports/MessagePortChannelProvider.h:
+        * dom/messageports/MessagePortChannelProviderImpl.cpp:
+        (WebCore::MessagePortChannelProviderImpl::checkRemotePortForActivity):
+        (WebCore::MessagePortChannelProviderImpl::checkProcessLocalPortForActivity):
+        (WebCore::MessagePortChannelProviderImpl::hasMessagesForPorts_temporarySync): Deleted.
+        * dom/messageports/MessagePortChannelProviderImpl.h:
+        
+        - Remove the lock and any background-thread code paths
+        - Add ASSERT(isMainThread())s throughout
+        * dom/messageports/MessagePortChannelRegistry.cpp:
+        (WebCore::MessagePortChannelRegistry::messagePortChannelCreated):
+        (WebCore::MessagePortChannelRegistry::messagePortChannelDestroyed):
+        (WebCore::MessagePortChannelRegistry::didEntangleLocalToRemote):
+        (WebCore::MessagePortChannelRegistry::didDisentangleMessagePort):
+        (WebCore::MessagePortChannelRegistry::didCloseMessagePort):
+        (WebCore::MessagePortChannelRegistry::didPostMessageToRemote):
+        (WebCore::MessagePortChannelRegistry::takeAllMessagesForPort):
+        (WebCore::MessagePortChannelRegistry::checkRemotePortForActivity):
+        (WebCore::MessagePortChannelRegistry::existingChannelContainingPort):
+        (WebCore::MessagePortChannelRegistry::hasMessagesForPorts_temporarySync): Deleted.
+        * dom/messageports/MessagePortChannelRegistry.h:
+
 2018-01-20  Andy Estes  <aestes@apple.com>
 
         [Apple Pay] Stop eagerly loading PassKit.framework
index a8d1390..7ec0ddc 100644 (file)
@@ -124,6 +124,8 @@ ExceptionOr<void> MessagePort::postMessage(JSC::ExecState& state, JSC::JSValue m
 {
     LOG(MessagePorts, "Attempting to post message to port %s (to be received by port %s)", m_identifier.logString().utf8().data(), m_remoteIdentifier.logString().utf8().data());
 
+    registerLocalActivity();
+
     Vector<RefPtr<MessagePort>> ports;
     auto messageData = SerializedScriptValue::create(state, messageValue, WTFMove(transfer), ports);
     if (messageData.hasException())
@@ -158,8 +160,10 @@ ExceptionOr<void> MessagePort::postMessage(JSC::ExecState& state, JSC::JSValue m
 void MessagePort::disentangle()
 {
     ASSERT(m_entangled);
-
     m_entangled = false;
+
+    registerLocalActivity();
+
     MessagePortChannelProvider::singleton().messagePortDisentangled(m_identifier);
 
     // We can't receive any messages or generate any events after this, so remove ourselves from the list of active ports.
@@ -171,6 +175,13 @@ void MessagePort::disentangle()
     m_scriptExecutionContext = nullptr;
 }
 
+void MessagePort::registerLocalActivity()
+{
+    // Any time certain local operations happen, we dirty our own state to delay GC.
+    m_hasHadLocalActivitySinceLastCheck = true;
+    m_mightBeEligibleForGC = false;
+}
+
 // Invoked to notify us that there are messages available for this port.
 // This code may be called from another thread, and so should not call any non-threadsafe APIs (i.e. should not call into the entangled channel or access mutable variables).
 void MessagePort::messageAvailable()
@@ -189,6 +200,8 @@ void MessagePort::start()
     if (!isEntangled())
         return;
 
+    registerLocalActivity();
+
     ASSERT(m_scriptExecutionContext);
     if (m_started)
         return;
@@ -199,6 +212,7 @@ void MessagePort::start()
 
 void MessagePort::close()
 {
+    m_mightBeEligibleForGC = true;
     if (m_closed)
         return;
 
@@ -210,6 +224,8 @@ void MessagePort::close()
 void MessagePort::contextDestroyed()
 {
     ASSERT(m_scriptExecutionContext);
+
+    m_mightBeEligibleForGC = true;
     if (!m_closed)
         close();
 
@@ -232,6 +248,9 @@ void MessagePort::dispatchMessages()
             if (!m_scriptExecutionContext)
                 return;
 
+            if (!messages.isEmpty())
+                registerLocalActivity();
+
             ASSERT(m_scriptExecutionContext->isContextThread());
 
             bool contextIsWorker = is<WorkerGlobalScope>(*m_scriptExecutionContext);
@@ -266,12 +285,59 @@ void MessagePort::dispatchMessages()
 
 bool MessagePort::hasPendingActivity() const
 {
-    // The spec says that entangled message ports should always be treated as if they have a strong reference.
-    // We'll also stipulate that the queue needs to be open (if the app drops its reference to the port before start()-ing it, then it's not really entangled as it's unreachable).
-    if (m_started && isEntangled() && MessagePortChannelProvider::singleton().hasMessagesForPorts_temporarySync(m_identifier, m_remoteIdentifier))
-        return true;
+    m_mightBeEligibleForGC = true;
+
+    // If the ScriptExecutionContext has been shut down on this object close()'ed, we can GC.
+    if (!m_scriptExecutionContext || m_closed)
+        return false;
+
+    // If this object has been idle since the remote port declared itself elgibile for GC, we can GC.
+    if (!m_hasHadLocalActivitySinceLastCheck && m_isRemoteEligibleForGC)
+        return false;
+
+    // If this MessagePort has no message event handler then the existence of remote activity cannot keep it alive.
+    if (!m_hasMessageEventListener)
+        return false;
+
+    // If we're not in the middle of asking the remote port about collectability, do so now.
+    if (!m_isAskingRemoteAboutGC) {
+        MessagePortChannelProvider::singleton().checkRemotePortForActivity(m_remoteIdentifier, [this, protectedThis = makeRef(*this)](MessagePortChannelProvider::HasActivity hasActivity) mutable {
+            auto innerHandler = [this, otherProtectedThis = WTFMove(protectedThis)](MessagePortChannelProvider::HasActivity hasActivity) {
+                bool hasHadLocalActivity = m_hasHadLocalActivitySinceLastCheck;
+                m_hasHadLocalActivitySinceLastCheck = false;
+
+                if (hasActivity == MessagePortChannelProvider::HasActivity::No && !hasHadLocalActivity)
+                    m_isRemoteEligibleForGC = true;
+
+                if (hasActivity == MessagePortChannelProvider::HasActivity::Yes)
+                    m_isRemoteEligibleForGC = false;
+
+                m_isAskingRemoteAboutGC = false;
+            };
+
+
+            if (!m_scriptExecutionContext)
+                return;
+
+            if (m_scriptExecutionContext->isContextThread()) {
+                innerHandler(hasActivity);
+                return;
+            }
 
-    return false;
+            m_scriptExecutionContext->postTask([innerHandler = WTFMove(innerHandler), hasActivity](ScriptExecutionContext&) mutable {
+                innerHandler(hasActivity);
+            });
+        });
+        m_isAskingRemoteAboutGC = true;
+    }
+
+    // Since we need an answer from the remote object, we have to pretend we have pending activity for now.
+    return true;
+}
+
+bool MessagePort::isLocallyReachable() const
+{
+    return !m_mightBeEligibleForGC;
 }
 
 MessagePort* MessagePort::locallyEntangledPort() const
@@ -323,11 +389,26 @@ Vector<RefPtr<MessagePort>> MessagePort::entanglePorts(ScriptExecutionContext& c
 
 bool MessagePort::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
 {
-    if (listener->isAttribute() && eventType == eventNames().messageEvent)
-        start();
+    if (eventType == eventNames().messageEvent) {
+        if (listener->isAttribute())
+            start();
+        m_hasMessageEventListener = true;
+        registerLocalActivity();
+    }
+
     return EventTargetWithInlineData::addEventListener(eventType, WTFMove(listener), options);
 }
 
+bool MessagePort::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
+{
+    auto result = EventTargetWithInlineData::removeEventListener(eventType, listener, options);
+
+    if (!hasEventListeners(eventNames().messageEvent))
+        m_hasMessageEventListener = false;
+
+    return result;
+}
+
 const char* MessagePort::activeDOMObjectName() const
 {
     return "MessagePort";
index 145a5a9..245ba82 100644 (file)
@@ -83,6 +83,8 @@ public:
     void stop() final { close(); }
     bool hasPendingActivity() const final;
 
+    bool isLocallyReachable() const;
+
     // EventTargetWithInlineData.
     EventTargetInterface eventTargetInterface() const final { return MessagePortEventTargetInterfaceType; }
     ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); }
@@ -93,9 +95,12 @@ private:
     explicit MessagePort(ScriptExecutionContext&, const MessagePortIdentifier& local, const MessagePortIdentifier& remote);
 
     bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) final;
+    bool removeEventListener(const AtomicString& eventType, EventListener&, const ListenerOptions&) final;
 
     void disentangle();
 
+    void registerLocalActivity();
+
     // A port starts out its life entangled, and remains entangled until it is closed or is cloned.
     bool isEntangled() const { return !m_closed && m_entangled; }
 
@@ -103,6 +108,13 @@ private:
     bool m_closed { false };
     bool m_entangled { true };
 
+    // Flags to manage querying the remote port for GC purposes
+    mutable bool m_mightBeEligibleForGC { false };
+    mutable bool m_hasHadLocalActivitySinceLastCheck { false };
+    mutable bool m_isRemoteEligibleForGC { false };
+    mutable bool m_isAskingRemoteAboutGC { false };
+    bool m_hasMessageEventListener { false };
+
     MessagePortIdentifier m_identifier;
     MessagePortIdentifier m_remoteIdentifier;
 
index 8703409..4dcac0f 100644 (file)
@@ -28,6 +28,8 @@
 
 #include "Logging.h"
 #include "MessagePortChannelRegistry.h"
+#include <wtf/CompletionHandler.h>
+#include <wtf/MainThread.h>
 
 namespace WebCore {
 
@@ -39,6 +41,8 @@ Ref<MessagePortChannel> MessagePortChannel::create(MessagePortChannelRegistry& r
 MessagePortChannel::MessagePortChannel(MessagePortChannelRegistry& registry, const MessagePortIdentifier& port1, const MessagePortIdentifier& port2)
     : m_registry(registry)
 {
+    ASSERT(isMainThread());
+
     relaxAdoptionRequirement();
 
     m_ports[0] = port1;
@@ -58,14 +62,14 @@ MessagePortChannel::~MessagePortChannel()
 
 bool MessagePortChannel::includesPort(const MessagePortIdentifier& port)
 {
-    Locker<Lock> locker(m_lock);
+    ASSERT(isMainThread());
 
     return m_ports[0] == port || m_ports[1] == port;
 }
 
 void MessagePortChannel::entanglePortWithProcess(const MessagePortIdentifier& port, ProcessIdentifier process)
 {
-    Locker<Lock> locker(m_lock);
+    ASSERT(isMainThread());
 
     LOG(MessagePorts, "MessagePortChannel %s (%p) entangling port %s", logString().utf8().data(), this, port.logString().utf8().data());
 
@@ -79,7 +83,7 @@ void MessagePortChannel::entanglePortWithProcess(const MessagePortIdentifier& po
 
 void MessagePortChannel::disentanglePort(const MessagePortIdentifier& port)
 {
-    Locker<Lock> locker(m_lock);
+    ASSERT(isMainThread());
 
     LOG(MessagePorts, "MessagePortChannel %s (%p) disentangling port %s", logString().utf8().data(), this, port.logString().utf8().data());
 
@@ -92,12 +96,11 @@ void MessagePortChannel::disentanglePort(const MessagePortIdentifier& port)
     // This set of steps is to guarantee that the lock is unlocked before the
     // last ref to this object is released.
     auto protectedThis = WTFMove(m_entangledToProcessProtectors[i]);
-    locker.unlockEarly();
 }
 
 void MessagePortChannel::closePort(const MessagePortIdentifier& port)
 {
-    Locker<Lock> locker(m_lock);
+    ASSERT(isMainThread());
 
     ASSERT(port == m_ports[0] || port == m_ports[1]);
     size_t i = port == m_ports[0] ? 0 : 1;
@@ -113,13 +116,11 @@ void MessagePortChannel::closePort(const MessagePortIdentifier& port)
     m_pendingMessagePortTransfers[i].clear();
     m_pendingMessageProtectors[i] = nullptr;
     m_entangledToProcessProtectors[i] = nullptr;
-
-    locker.unlockEarly();
 }
 
 bool MessagePortChannel::postMessageToRemote(MessageWithMessagePorts&& message, const MessagePortIdentifier& remoteTarget)
 {
-    Locker<Lock> locker(m_lock);
+    ASSERT(isMainThread());
 
     ASSERT(remoteTarget == m_ports[0] || remoteTarget == m_ports[1]);
     size_t i = remoteTarget == m_ports[0] ? 0 : 1;
@@ -156,7 +157,7 @@ bool MessagePortChannel::postMessageToRemote(MessageWithMessagePorts&& message,
 
 void MessagePortChannel::takeAllMessagesForPort(const MessagePortIdentifier& port, Function<void(Vector<MessageWithMessagePorts>&&, Function<void()>&&)>&& callback)
 {
-    Locker<Lock> locker(m_lock);
+    ASSERT(isMainThread());
 
     LOG(MessagePorts, "MessagePortChannel %p taking all messages for port %s", this, port.logString().utf8().data());
 
@@ -181,7 +182,6 @@ void MessagePortChannel::takeAllMessagesForPort(const MessagePortIdentifier& por
     HashSet<RefPtr<MessagePortChannel>> transferredPortProtectors;
     transferredPortProtectors.swap(m_pendingMessagePortTransfers[i]);
 
-    locker.unlockEarly();
     callback(WTFMove(result), [size, this, port, protectedThis = WTFMove(m_pendingMessageProtectors[i]), transferredPortProtectors = WTFMove(transferredPortProtectors)] {
         UNUSED_PARAM(port);
 #if LOG_DISABLED
@@ -193,10 +193,51 @@ void MessagePortChannel::takeAllMessagesForPort(const MessagePortIdentifier& por
     });
 }
 
-bool MessagePortChannel::hasAnyMessagesPendingOrInFlight() const
+void MessagePortChannel::checkRemotePortForActivity(const MessagePortIdentifier& remotePort, CompletionHandler<void(MessagePortChannelProvider::HasActivity)>&& callback)
 {
-    Locker<Lock> locker(m_lock);
+    ASSERT(isMainThread());
+    ASSERT(remotePort == m_ports[0] || remotePort == m_ports[1]);
+
+    // If the remote port is closed there is no pending activity.
+    size_t i = remotePort == m_ports[0] ? 0 : 1;
+    if (m_isClosed[i]) {
+        callback(MessagePortChannelProvider::HasActivity::No);
+        return;
+    }
 
+    // If there are any messages in flight between the ports, there is pending activity.
+    if (hasAnyMessagesPendingOrInFlight()) {
+        callback(MessagePortChannelProvider::HasActivity::Yes);
+        return;
+    }
+
+    // If the port is not currently in a process then it's being transferred as part of a postMessage.
+    // We treat these ports as if they do have activity since they will be revived when the message is delivered.
+    if (!m_processes[i]) {
+        callback(MessagePortChannelProvider::HasActivity::Yes);
+        return;
+    }
+
+    auto outerCallback = CompletionHandler<void(MessagePortChannelProvider::HasActivity)> { [this, protectedThis = makeRef(*this), callback = WTFMove(callback)] (MessagePortChannelProvider::HasActivity hasActivity) {
+        if (hasActivity == MessagePortChannelProvider::HasActivity::Yes) {
+            callback(hasActivity);
+            return;
+        }
+
+        // If the remote port said it had no activity, check again for any messages that might be in flight.
+        // This is because it might have asynchronously sent a message just before it was asked about local activity.
+        if (hasAnyMessagesPendingOrInFlight())
+            hasActivity = MessagePortChannelProvider::HasActivity::Yes;
+
+        callback(hasActivity);
+    } };
+
+    MessagePortChannelProvider::singleton().checkProcessLocalPortForActivity(remotePort, *m_processes[i], WTFMove(outerCallback));
+}
+
+bool MessagePortChannel::hasAnyMessagesPendingOrInFlight() const
+{
+    ASSERT(isMainThread());
     return m_messageBatchesInFlight || !m_pendingMessages[0].isEmpty() || !m_pendingMessages[1].isEmpty();
 }
 
index 2146bd0..4f2e999 100644 (file)
 
 #pragma once
 
+#include "MessagePortChannelProvider.h"
 #include "MessagePortIdentifier.h"
 #include "MessageWithMessagePorts.h"
 #include "Process.h"
 #include <wtf/HashSet.h>
-#include <wtf/Lock.h>
 #include <wtf/RefCounted.h>
 #include <wtf/text/WTFString.h>
 
@@ -53,6 +53,7 @@ public:
     bool postMessageToRemote(MessageWithMessagePorts&&, const MessagePortIdentifier& remoteTarget);
 
     void takeAllMessagesForPort(const MessagePortIdentifier&, Function<void(Vector<MessageWithMessagePorts>&&, Function<void()>&&)>&&);
+    void checkRemotePortForActivity(const MessagePortIdentifier&, CompletionHandler<void(MessagePortChannelProvider::HasActivity)>&& callback);
 
     bool hasAnyMessagesPendingOrInFlight() const;
 
@@ -65,10 +66,6 @@ public:
 private:
     MessagePortChannel(MessagePortChannelRegistry&, const MessagePortIdentifier& port1, const MessagePortIdentifier& port2);
 
-    // FIXME: This lock is to temporarily support synchronous background-thread GC.
-    // It should be removed and this class relegated to main thread only.
-    mutable Lock m_lock;
-
     MessagePortIdentifier m_ports[2];
     bool m_isClosed[2] { false, false };
     std::optional<ProcessIdentifier> m_processes[2];
index 9042e07..38984da 100644 (file)
@@ -25,6 +25,7 @@
 
 #pragma once
 
+#include "Process.h"
 #include <wtf/Function.h>
 #include <wtf/Vector.h>
 
@@ -40,6 +41,7 @@ public:
 
     virtual ~MessagePortChannelProvider() { }
 
+    // Operations that WebProcesses perform
     virtual void createNewMessagePortChannel(const MessagePortIdentifier& local, const MessagePortIdentifier& remote) = 0;
     virtual void entangleLocalPortInThisProcessToRemote(const MessagePortIdentifier& local, const MessagePortIdentifier& remote) = 0;
     virtual void messagePortDisentangled(const MessagePortIdentifier& local) = 0;
@@ -47,7 +49,14 @@ public:
     virtual void takeAllMessagesForPort(const MessagePortIdentifier&, Function<void(Vector<MessageWithMessagePorts>&&, Function<void()>&&)>&&) = 0;
     virtual void postMessageToRemote(MessageWithMessagePorts&&, const MessagePortIdentifier& remoteTarget) = 0;
 
-    virtual bool hasMessagesForPorts_temporarySync(const MessagePortIdentifier&, const MessagePortIdentifier&) = 0;
+    enum class HasActivity {
+        Yes,
+        No,
+    };
+    virtual void checkRemotePortForActivity(const MessagePortIdentifier& remoteTarget, CompletionHandler<void(HasActivity)>&& callback) = 0;
+
+    // Operations that the coordinating process performs (e.g. the UIProcess)
+    virtual void checkProcessLocalPortForActivity(const MessagePortIdentifier&, ProcessIdentifier, CompletionHandler<void(HasActivity)>&&) = 0;
 
 private:
 
index e0a1463..fedafc9 100644 (file)
@@ -27,6 +27,7 @@
 #include "MessagePortChannelProviderImpl.h"
 
 #include "MessagePort.h"
+#include <wtf/CompletionHandler.h>
 #include <wtf/MainThread.h>
 #include <wtf/RunLoop.h>
 
@@ -97,10 +98,30 @@ void MessagePortChannelProviderImpl::takeAllMessagesForPort(const MessagePortIde
     });
 }
 
-bool MessagePortChannelProviderImpl::hasMessagesForPorts_temporarySync(const MessagePortIdentifier& port1, const MessagePortIdentifier& port2)
+void MessagePortChannelProviderImpl::checkRemotePortForActivity(const MessagePortIdentifier& remoteTarget, CompletionHandler<void(HasActivity)>&& outerCallback)
 {
-    // FIXME: Remove this sync function call when GC logic is made asynchronous.
-    return m_registry.hasMessagesForPorts_temporarySync(port1, port2);
+    auto callback = CompletionHandler<void(HasActivity)> { [outerCallback = WTFMove(outerCallback)](HasActivity hasActivity) {
+        ASSERT(isMainThread());
+        outerCallback(hasActivity);
+    } };
+
+    performActionOnMainThread([registry = &m_registry, remoteTarget, callback = WTFMove(callback)]() mutable {
+        registry->checkRemotePortForActivity(remoteTarget, WTFMove(callback));
+    });
 }
 
+void MessagePortChannelProviderImpl::checkProcessLocalPortForActivity(const MessagePortIdentifier& identifier, ProcessIdentifier, CompletionHandler<void(HasActivity)>&& callback)
+{
+    ASSERT(isMainThread());
+
+    auto port = MessagePort::existingMessagePortForIdentifier(identifier);
+    if (!port) {
+        callback(MessagePortChannelProvider::HasActivity::No);
+        return;
+    }
+
+    callback(port->isLocallyReachable() ? HasActivity::Yes : HasActivity::No);
+}
+
+
 } // namespace WebCore
index de71298..b4771f4 100644 (file)
@@ -41,8 +41,9 @@ private:
     void messagePortClosed(const MessagePortIdentifier& local) final;
     void postMessageToRemote(MessageWithMessagePorts&&, const MessagePortIdentifier& remoteTarget) final;
     void takeAllMessagesForPort(const MessagePortIdentifier&, Function<void(Vector<MessageWithMessagePorts>&&, Function<void()>&&)>&&) final;
+    void checkRemotePortForActivity(const MessagePortIdentifier& remoteTarget, CompletionHandler<void(HasActivity)>&& callback) final;
 
-    bool hasMessagesForPorts_temporarySync(const MessagePortIdentifier&, const MessagePortIdentifier&) final;
+    void checkProcessLocalPortForActivity(const MessagePortIdentifier&, ProcessIdentifier, CompletionHandler<void(HasActivity)>&&) final;
 
     void performActionOnMainThread(Function<void()>&&);
 
index 7572610..c77a8ff 100644 (file)
@@ -45,7 +45,6 @@ void MessagePortChannelRegistry::didCreateMessagePortChannel(const MessagePortId
 
 void MessagePortChannelRegistry::messagePortChannelCreated(MessagePortChannel& channel)
 {
-    Locker<Lock> locker(m_openChannelsLock);
     ASSERT(isMainThread());
 
     auto result = m_openChannels.ensure(channel.port1(), [channel = &channel] {
@@ -61,7 +60,6 @@ void MessagePortChannelRegistry::messagePortChannelCreated(MessagePortChannel& c
 
 void MessagePortChannelRegistry::messagePortChannelDestroyed(MessagePortChannel& channel)
 {
-    Locker<Lock> locker(m_openChannelsLock);
     ASSERT(isMainThread());
 
     ASSERT(m_openChannels.get(channel.port1()) == &channel);
@@ -76,7 +74,6 @@ void MessagePortChannelRegistry::messagePortChannelDestroyed(MessagePortChannel&
 void MessagePortChannelRegistry::didEntangleLocalToRemote(const MessagePortIdentifier& local, const MessagePortIdentifier& remote, ProcessIdentifier process)
 {
     ASSERT(isMainThread());
-    Locker<Lock> locker(m_openChannelsLock);
 
     // The channel might be gone if the remote side was closed.
     auto* channel = m_openChannels.get(local);
@@ -91,21 +88,18 @@ void MessagePortChannelRegistry::didEntangleLocalToRemote(const MessagePortIdent
 void MessagePortChannelRegistry::didDisentangleMessagePort(const MessagePortIdentifier& port)
 {
     ASSERT(isMainThread());
-    Locker<Lock> locker(m_openChannelsLock);
 
     // The channel might be gone if the remote side was closed.
     auto* channel = m_openChannels.get(port);
     if (!channel)
         return;
 
-    locker.unlockEarly();
     channel->disentanglePort(port);
 }
 
 void MessagePortChannelRegistry::didCloseMessagePort(const MessagePortIdentifier& port)
 {
     ASSERT(isMainThread());
-    Locker<Lock> locker(m_openChannelsLock);
 
     LOG(MessagePorts, "Registry: MessagePort %s closed in registry", port.logString().utf8().data());
 
@@ -118,7 +112,6 @@ void MessagePortChannelRegistry::didCloseMessagePort(const MessagePortIdentifier
         LOG(MessagePorts, "Registry: (Note) The channel closed for port %s had messages pending or in flight", port.logString().utf8().data());
 #endif
 
-    locker.unlockEarly();
     channel->closePort(port);
 
     // FIXME: When making message ports be multi-process, this should probably push a notification
@@ -128,7 +121,6 @@ void MessagePortChannelRegistry::didCloseMessagePort(const MessagePortIdentifier
 bool MessagePortChannelRegistry::didPostMessageToRemote(MessageWithMessagePorts&& message, const MessagePortIdentifier& remoteTarget)
 {
     ASSERT(isMainThread());
-    Locker<Lock> locker(m_openChannelsLock);
 
     LOG(MessagePorts, "Registry: Posting message to MessagePort %s in registry", remoteTarget.logString().utf8().data());
 
@@ -139,14 +131,12 @@ bool MessagePortChannelRegistry::didPostMessageToRemote(MessageWithMessagePorts&
         return false;
     }
 
-    locker.unlockEarly();
     return channel->postMessageToRemote(WTFMove(message), remoteTarget);
 }
 
 void MessagePortChannelRegistry::takeAllMessagesForPort(const MessagePortIdentifier& port, Function<void(Vector<MessageWithMessagePorts>&&, Function<void()>&&)>&& callback)
 {
     ASSERT(isMainThread());
-    Locker<Lock> locker(m_openChannelsLock);
 
     LOG(MessagePorts, "Registry: Taking all messages for MessagePort %s", port.logString().utf8().data());
 
@@ -157,35 +147,27 @@ void MessagePortChannelRegistry::takeAllMessagesForPort(const MessagePortIdentif
         return;
     }
 
-    locker.unlockEarly();
     channel->takeAllMessagesForPort(port, WTFMove(callback));
 }
 
-bool MessagePortChannelRegistry::hasMessagesForPorts_temporarySync(const MessagePortIdentifier& port1, const MessagePortIdentifier& port2)
+void MessagePortChannelRegistry::checkRemotePortForActivity(const MessagePortIdentifier& remoteTarget, CompletionHandler<void(MessagePortChannelProvider::HasActivity)>&& callback)
 {
-    // FIXME: Remove this function (and the lock) with a followup patch to do async garbage collection
-    Locker<Lock> locker(m_openChannelsLock);
-
-    auto* channel1 = m_openChannels.get(port1);
-    if (!channel1)
-        return false;
-
-    auto* channel2 = m_openChannels.get(port2);
-    ASSERT_UNUSED(channel2, channel2);
-    if (!channel2)
-        return false;
+    ASSERT(isMainThread());
 
-    ASSERT(channel1 == channel2);
+    // The channel might be gone if the remote side was closed.
+    auto* channel = m_openChannels.get(remoteTarget);
+    if (!channel) {
+        callback(MessagePortChannelProvider::HasActivity::No);
+        return;
+    }
 
-    return channel1->hasAnyMessagesPendingOrInFlight();
+    channel->checkRemotePortForActivity(remoteTarget, WTFMove(callback));
 }
 
 MessagePortChannel* MessagePortChannelRegistry::existingChannelContainingPort(const MessagePortIdentifier& port)
 {
     ASSERT(isMainThread());
 
-    Locker<Lock> locker(m_openChannelsLock);
-
     return m_openChannels.get(port);
 }
 
index 45d0a57..f10ecb5 100644 (file)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "MessagePortChannel.h"
+#include "MessagePortChannelProvider.h"
 #include "MessagePortIdentifier.h"
 #include "Process.h"
 #include <wtf/HashMap.h>
@@ -42,18 +43,14 @@ public:
     void didCloseMessagePort(const MessagePortIdentifier& local);
     bool didPostMessageToRemote(MessageWithMessagePorts&&, const MessagePortIdentifier& remoteTarget);
     void takeAllMessagesForPort(const MessagePortIdentifier&, Function<void(Vector<MessageWithMessagePorts>&&, Function<void()>&&)>&&);
+    void checkRemotePortForActivity(const MessagePortIdentifier& remoteTarget, CompletionHandler<void(MessagePortChannelProvider::HasActivity)>&& callback);
 
     MessagePortChannel* existingChannelContainingPort(const MessagePortIdentifier&);
-    bool hasMessagesForPorts_temporarySync(const MessagePortIdentifier&, const MessagePortIdentifier&);
 
     void messagePortChannelCreated(MessagePortChannel&);
     void messagePortChannelDestroyed(MessagePortChannel&);
 
 private:
-
-    // FIXME: The need for the open channels lock is temporary.
-    // It should be removed and this class should be main-thread only.
-    Lock m_openChannelsLock;
     HashMap<MessagePortIdentifier, MessagePortChannel*> m_openChannels;
 };