Commit a12e4afc authored by levin@chromium.org's avatar levin@chromium.org

WebCore:

2009-06-21  Drew Wilson  <atwilson@google.com>

        Reviewed by David Levin.

        <https://bugs.webkit.org/show_bug.cgi?id=26448>

        Added optimized GC for MessagePorts when the entangled port is run by the same thread.
        Fixed bug in isProxyFor() that was not properly throwing an exception when trying to clone the entangled port.

        * bindings/js/JSDOMBinding.cpp:
        (WebCore::markActiveObjectsForContext):
        Now marks remotely entangled ports as in-use, in addition to those with pending activity.
        * bindings/js/JSMessagePortCustom.cpp:
        (WebCore::JSMessagePort::mark):
        Now checks if the entangled port is local (run by same thread) and if so mark()s it.
        * dom/MessagePort.cpp:
        (WebCore::MessagePort::postMessage):
        (WebCore::MessagePort::disentangle):
        Removes cloned ports from the ScriptExecutionContext - this allows cloned ports to be GC'd as otherwise they look like remotely entangled ports.
        (WebCore::MessagePort::start):
        (WebCore::MessagePort::locallyEntangledPort):
        Added API for fetching the entangled port if it is run by the same thread
        * dom/MessagePort.h:
        * dom/MessagePortProxyWrapper.h:
        * dom/default/MessagePortProxy.cpp:
        (WebCore::MessagePortProxyWrapper::locallyEntangledPort):
        Added API for fetching the entangled port if it is run by the same thread
        (WebCore::MessagePortProxy::hasPendingActivity):
        Changed definition of hasPendingActivity() to be stricter - only returns true if there are pending messages.
        (WebCore::MessagePortProxy::locallyEntangledPort):
        * dom/default/MessagePortProxy.h:

LayoutTests:

2009-06-21  Drew Wilson  <atwilson@google.com>

        Reviewed by David Levin.

        <https://bugs.webkit.org/show_bug.cgi?id=26448>

        New tests for MessagePort GC cases that weren't previously covered.

        * fast/events/message-channel-gc-4-expected.txt: Added.
        Test for case where both MessagePorts are in-transit (cloned) when a GC occurs.
        * fast/events/message-channel-gc-4.html-disabled: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@44916 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 3affb89e
2009-06-21 Drew Wilson <atwilson@google.com>
Reviewed by David Levin.
<https://bugs.webkit.org/show_bug.cgi?id=26448>
New tests for MessagePort GC cases that weren't previously covered.
* fast/events/message-channel-gc-4-expected.txt: Added.
Test for case where both MessagePorts are in-transit (cloned) when a GC occurs.
* fast/events/message-channel-gc-4.html-disabled: Added.
2009-06-21 Drew Wilson <atwilson@google.com>
Reviewed by David Levin.
......
Test that MessagePort messages are delivered even if both ports are in transit (cloned).
Should say PASS twice.
PASS: Received message to cloned port.
PASS: Received message from cloned port.
<body>
<p>Test that MessagePort messages are delivered even if both ports are in transit (cloned).</p>
<p>Should say PASS twice.</p>
<pre id=log></pre>
<script>
function gc()
{
if (window.GCController)
return GCController.collect();
for (var i = 0; i < 10000; i++) { // force garbage collection (FF requires about 9K allocations before a collect)
var s = new String("abc");
}
}
function log(message)
{
document.getElementById("log").innerHTML += message + "<br>";
}
if (window.layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
function test1()
{
var channel = new MessageChannel;
var channel2 = new MessageChannel;
channel.port1.postMessage("msg1");
channel2.port1.postMessage("", channel.port1);
channel2.port2.postMessage("", channel.port2);
channel2.port2.onmessage = channel2.port1.onmessage = function(evt) {
evt.messagePort.onmessage = function(event) {
if (event.data == "msg1") {
log("PASS: Received message to cloned port.");
this.postMessage("msg2");
gc();
} else if (event.data == "msg2") {
log("PASS: Received message from cloned port.");
gc();
if (window.layoutTestController)
layoutTestController.notifyDone();
} else {
log("FAIL: Received unknown message: " + event.data);
}
}
evt.messagePort = 0;
}
channel = 0;
channel2 = 0;
gc();
}
test1();
</script>
</body>
2009-06-21 Drew Wilson <atwilson@google.com>
Reviewed by David Levin.
<https://bugs.webkit.org/show_bug.cgi?id=26448>
Added optimized GC for MessagePorts when the entangled port is run by the same thread.
Fixed bug in isProxyFor() that was not properly throwing an exception when trying to clone the entangled port.
* bindings/js/JSDOMBinding.cpp:
(WebCore::markActiveObjectsForContext):
Now marks remotely entangled ports as in-use, in addition to those with pending activity.
* bindings/js/JSMessagePortCustom.cpp:
(WebCore::JSMessagePort::mark):
Now checks if the entangled port is local (run by same thread) and if so mark()s it.
* dom/MessagePort.cpp:
(WebCore::MessagePort::postMessage):
(WebCore::MessagePort::disentangle):
Removes cloned ports from the ScriptExecutionContext - this allows cloned ports to be GC'd as otherwise they look like remotely entangled ports.
(WebCore::MessagePort::start):
(WebCore::MessagePort::locallyEntangledPort):
Added API for fetching the entangled port if it is run by the same thread
* dom/MessagePort.h:
* dom/MessagePortProxyWrapper.h:
* dom/default/MessagePortProxy.cpp:
(WebCore::MessagePortProxyWrapper::locallyEntangledPort):
Added API for fetching the entangled port if it is run by the same thread
(WebCore::MessagePortProxy::hasPendingActivity):
Changed definition of hasPendingActivity() to be stricter - only returns true if there are pending messages.
(WebCore::MessagePortProxy::locallyEntangledPort):
* dom/default/MessagePortProxy.h:
2009-06-21 Drew Wilson <atwilson@google.com>
Reviewed by David Levin.
......
......@@ -325,10 +325,10 @@ void markActiveObjectsForContext(JSGlobalData& globalData, ScriptExecutionContex
const HashSet<MessagePort*>& messagePorts = scriptExecutionContext->messagePorts();
HashSet<MessagePort*>::const_iterator portsEnd = messagePorts.end();
for (HashSet<MessagePort*>::const_iterator iter = messagePorts.begin(); iter != portsEnd; ++iter) {
if ((*iter)->hasPendingActivity()) {
// If the message port is remotely entangled, then always mark it as in-use because we can't determine reachability across threads.
if (!(*iter)->locallyEntangledPort() || (*iter)->hasPendingActivity()) {
DOMObject* wrapper = getCachedDOMObjectWrapper(globalData, *iter);
// A port with pending activity must have a wrapper to mark its listeners, so no null check.
if (!wrapper->marked())
if (wrapper && !wrapper->marked())
wrapper->mark();
}
}
......
......@@ -44,6 +44,13 @@ void JSMessagePort::mark()
markIfNotNull(m_impl->onmessage());
// If we have a locally entangled port, we can directly mark it as reachable. Ports that are remotely entangled are marked in-use by markActiveObjectsForContext().
if (MessagePort* entangledPort = m_impl->locallyEntangledPort()) {
DOMObject* wrapper = getCachedDOMObjectWrapper(*Heap::heap(this)->globalData(), entangledPort);
if (wrapper && !wrapper->marked())
wrapper->mark();
}
typedef MessagePort::EventListenersMap EventListenersMap;
typedef MessagePort::ListenerVector ListenerVector;
EventListenersMap& eventListeners = m_impl->eventListeners();
......
......@@ -62,9 +62,9 @@ void MessagePort::postMessage(const String& message, ExceptionCode& ec)
void MessagePort::postMessage(const String& message, MessagePort* dataPort, ExceptionCode& ec)
{
ASSERT(m_scriptExecutionContext);
if (!m_entangledChannel)
return;
ASSERT(m_scriptExecutionContext);
OwnPtr<MessagePortChannel> channel;
if (dataPort) {
......@@ -83,8 +83,14 @@ PassOwnPtr<MessagePortChannel> MessagePort::disentangle(ExceptionCode& ec)
{
if (!m_entangledChannel)
ec = INVALID_STATE_ERR;
else
else {
m_entangledChannel->disentangle();
// We can't receive any messages or generate any events, so remove ourselves from the list of active ports.
ASSERT(m_scriptExecutionContext);
m_scriptExecutionContext->destroyedMessagePort(this);
m_scriptExecutionContext = 0;
}
return m_entangledChannel.release();
}
......@@ -98,6 +104,10 @@ void MessagePort::messageAvailable()
void MessagePort::start()
{
// Do nothing if we've been cloned
if (!m_entangledChannel)
return;
ASSERT(m_scriptExecutionContext);
if (m_started)
return;
......@@ -230,4 +240,9 @@ bool MessagePort::hasPendingActivity()
return m_started && m_entangledChannel && m_entangledChannel->hasPendingActivity();
}
MessagePort* MessagePort::locallyEntangledPort()
{
return m_entangledChannel ? m_entangledChannel->locallyEntangledPort(m_scriptExecutionContext) : 0;
}
} // namespace WebCore
......@@ -87,6 +87,12 @@ namespace WebCore {
void setOnmessage(PassRefPtr<EventListener>);
EventListener* onmessage() const { return m_onMessageListener.get(); }
// Returns null if there is no entangled port, or if the entangled port is run by a different thread.
// Returns null otherwise.
// NOTE: This is used solely to enable a GC optimization. Some platforms may not be able to determine ownership of the remote port (since it may live cross-process) - those platforms may always return null.
MessagePort* locallyEntangledPort();
bool isEntangled() { return m_entangledChannel; }
private:
MessagePort(ScriptExecutionContext&);
......
......@@ -43,6 +43,7 @@ namespace WebCore {
class MessagePort;
class PlatformMessagePortChannel;
class ScriptExecutionContext;
class String;
// MessagePortChannel is a platform-independent interface to the remote side of a message channel.
......@@ -65,7 +66,7 @@ namespace WebCore {
// Used by MessagePort.postMessage() to prevent callers from passing a port's own entangled port.
bool isConnectedTo(MessagePort*);
// Returns true if the channel currently contains messages for this port, or may receive messages in the future (is open).
// Returns true if the proxy currently contains messages for this port.
bool hasPendingActivity();
class EventData {
......@@ -87,6 +88,9 @@ namespace WebCore {
// Extracts a message from the message queue for this port.
bool tryGetMessageFromRemote(OwnPtr<EventData>&);
// Returns the entangled port if run by the same thread (see MessagePort::locallyEntangledPort() for more details).
MessagePort* locallyEntangledPort(const ScriptExecutionContext*);
~MessagePortChannel();
private:
......
......@@ -32,6 +32,7 @@
#include "PlatformMessagePortChannel.h"
#include "MessagePort.h"
#include "ScriptExecutionContext.h"
namespace WebCore {
......@@ -71,6 +72,10 @@ bool MessagePortChannel::hasPendingActivity()
return m_channel->hasPendingActivity();
}
MessagePort* MessagePortChannel::locallyEntangledPort(const ScriptExecutionContext* context)
{
return m_channel->locallyEntangledPort(context);
}
PassRefPtr<PlatformMessagePortChannel> PlatformMessagePortChannel::create(PassRefPtr<MessagePortQueue> incoming, PassRefPtr<MessagePortQueue> outgoing)
{
......@@ -198,7 +203,20 @@ void PlatformMessagePortChannel::closeInternal()
bool PlatformMessagePortChannel::hasPendingActivity()
{
MutexLocker lock(m_mutex);
return m_entangledChannel || !m_incomingQueue->isEmpty();
return !m_incomingQueue->isEmpty();
}
MessagePort* PlatformMessagePortChannel::locallyEntangledPort(const ScriptExecutionContext* context)
{
MutexLocker lock(m_mutex);
// See if both contexts are run by the same thread (are the same context, or are both documents).
if (m_remotePort) {
// The remote port's ScriptExecutionContext is guaranteed not to change here - MessagePort::contextDestroyed() will close the port before the context goes away, and close() will block because we are holding the mutex.
ScriptExecutionContext* remoteContext = m_remotePort->scriptExecutionContext();
if (remoteContext == context || (remoteContext && remoteContext->isDocument() && context->isDocument()))
return m_remotePort;
}
return 0;
}
} // namespace WebCore
......@@ -46,30 +46,17 @@ namespace WebCore {
// The goal of this implementation is to eliminate contention except when cloning or closing the port, so each side of the channel has its own separate mutex.
class PlatformMessagePortChannel : public ThreadSafeShared<PlatformMessagePortChannel> {
public:
// Creates a channel entangling two ports.
static void createChannel(PassRefPtr<MessagePort>, PassRefPtr<MessagePort>);
// Entangles the channel with a port (called when a port has been cloned, after the clone has been marshalled to its new owning thread and is ready to receive messages).
// Returns false if the entanglement failed because the port was closed.
// APIs delegated from MessagePortChannel.h
bool entangleIfOpen(MessagePort*);
// Disentangles the channel from a given port so it no longer forwards messages to the port. Called when the port is being cloned and no new owning thread has yet been established.
void disentangle();
// Sends a message and optional cloned port to the remote port.
void postMessageToRemote(PassOwnPtr<MessagePortChannel::EventData>);
// Extracts a message from the message queue for this port.
bool tryGetMessageFromRemote(OwnPtr<MessagePortChannel::EventData>&);
// Closes the port (ensures that no further messages can be added to either queue).
void close();
// Used by MessagePort.postMessage() to prevent callers from passing a port's own entangled port.
bool isConnectedTo(MessagePort*);
// Returns true if the channel currently contains messages for this port, or may receive messages in the future (is open).
bool hasPendingActivity();
MessagePort* locallyEntangledPort(const ScriptExecutionContext*);
// Wrapper for MessageQueue that allows us to do thread safe sharing by two proxies.
class MessagePortQueue : public ThreadSafeShared<MessagePortQueue> {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment