I "wrote" most of this code in the sense that I took the old garbage
collector design and refactored it to make isolated worlds possible.
I don't understand the MessagePorts lifetime issues in detail,
however.

Adam


On Fri, Nov 20, 2009 at 7:02 PM, Drew Wilson <atwil...@chromium.org> wrote:
> So I looked into the problem further - the issue was that calling close()
> would still leave MessagePorts in a seemingly entangled state, which meant
> that they would never be freed (turns out this caused some related issues on
> the WebKit side related to delaying worker exit).
> I updated the code so that MessagePorts now are disentangled when they are
> close()-d, and now I'm getting a failed assertion in the V8GCPrologue code
> in the WebKit v8 bindings.
> Here's the sequence of events:
> 1) I hit reload on a page - this forces a GC to clean up the old context and
> related objects.
> 2) GCPrologueVisitor is called by our GCPrologueCallback for a set of
> objects. It has code that looks like this:
>     void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper)
>     {
>         ASSERT(wrapper.IsWeak());
>         V8ClassIndex::V8WrapperType type =
> V8DOMWrapper::domWrapperType(wrapper);
>         ...
>         if (type == V8ClassIndex::MESSAGEPORT) {
>           MessagePort* port1 = static_cast<MessagePort*>(object);
>           if (port1->isEntangled())
>               wrapper.ClearWeak();
>        ...
>     }
> The end result is that if MessagePort::isEntangled() returns true, the
> MessagePort has its Weak flag cleared, and is not garbage collected.
> 3) GC happens
> 4) After garbage collection, GCEpilogueVisitor is called via our
> GCEpilogueCallback. It walks through all the various nodes, and does this:
>     void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper)
>     {
>         V8ClassIndex::V8WrapperType type =
> V8DOMWrapper::domWrapperType(wrapper);
>         ....
>         if (type == V8ClassIndex::MESSAGEPORT) {
>             MessagePort* port1 = static_cast<MessagePort*>(object);
>             if (port1->isEntangled()) {
>                 // We marked this port as reachable in GCPrologueVisitor.
>  Undo this now since the
>                 // port could be not reachable in the future if it gets
> disentangled (and also
>                 // GCPrologueVisitor expects to see all handles marked as
> weak).
>                 wrapper.MakeWeak(port1,
> &DOMDataStore::weakActiveDOMObjectCallback);
>             }
>             ...
>        }
> Looks hunky-dory, except... with my latest change, isEntangled() can now
> (correctly) change state when close() is called. What happens in step 3 is
> garbage collection causes the ScriptExecutionContext (Document object) for
> the old page to get freed. As part of this, MessagePort::close() is invoked
> on all of the message ports associated with that context. So they are
> entangled in step 2, so ClearWeak is called on them, but they *aren't*
> entangled in step 4, so MarkWeak is no longer called. The next time we do a
> GC, we die due to the ASSERT(wrapper->IsWeak()) in GCPrologueVisitor.
> So I'm not sure exactly what the right fix is here, and I'm hoping that
> perhaps someone who wrote this code initially could chime in - I'm thinking
> that we should just call MarkWeak on all MessagePorts in GCEpilogue (since
> GCEpilogue wouldn't be called on them if they weren't supposed to be weak),
> but I'm not sure if that will have any adverse side-effects if there are
> references. For example, if I do this:
> var p = new MessageChannel().port1;
> p.close();
> I'd still like that MessagePort to hang around as long as there's a
> reachable reference (i.e. "p").
> Any thoughts from someone more familiar with this code?
> -atw
>
> On Thu, Nov 19, 2009 at 11:07 PM, Mads Ager <a...@google.com> wrote:
>>
>> Let us know what you find Drew.  I'll be happy to help figure this out as
>> well.
>>
>> -- Mads
>>
>> On Thu, Nov 19, 2009 at 11:46 PM, Drew Wilson <atwil...@chromium.org>
>> wrote:
>> > There's no bug on this yet - I ran across this when I ran out of memory
>> > in
>> > my SharedWorkers soak test I was running (turns out that this bug may be
>> > unrelated to the SharedWorkers bug).
>> > It's quite possible that this is related to the V8GCController code -
>> > I'd
>> > forgotten about that. In fact, it's quite possible that the true cause
>> > is
>> > "ports are not getting closed when their parent document exits", in
>> > which
>> > case leaking the port is the expected behavior. I'll look at this a bit
>> > more
>> > - thanks for the pointer.
>> > -atw
>> >
>> > On Thu, Nov 19, 2009 at 2:35 PM, Vitaly Repeshko <vita...@chromium.org>
>> > wrote:
>> >>
>> >> On Fri, Nov 20, 2009 at 1:20 AM, Drew Wilson <atwil...@chromium.org>
>> >> wrote:
>> >> > I'm investigating a leak in the MessagePort/MessageChannel code.
>> >> > Basically,
>> >> > if I have a page that looks like this:
>> >> > <script>
>> >> > new MessageChannel();
>> >> > </script>
>> >> > We leak two MessagePorts every time we reload the page.
>> >> > The WebCore::MessageChannel impl object has two RefPtrs to two
>> >> > MessagePort
>> >> > objects. When I reload the page, the MessageChannel DOMWrapper V8
>> >> > object
>> >> > is
>> >> > GC'd, leading to the MessageChannel impl object getting deref'd and
>> >> > freed.
>> >> > The problem is that the V8 MessageChannel constructor has this code:
>> >> >
>> >> >     // Create references from the MessageChannel wrapper to the two
>> >> >     // MessagePort wrappers to make sure that the MessagePort
>> >> > wrappers
>> >> >     // stay alive as long as the MessageChannel wrapper is around.
>> >> >     messageChannel->SetInternalField(kMessageChannelPort1Index,
>> >> > V8DOMWrapper::convertToV8Object(V8ClassIndex::MESSAGEPORT,
>> >> > obj->port1()));
>> >> >     messageChannel->SetInternalField(kMessageChannelPort2Index,
>> >> > V8DOMWrapper::convertToV8Object(V8ClassIndex::MESSAGEPORT,
>> >> > obj->port2()));
>> >> >     // Setup the standard wrapper object internal fields.
>> >> >     V8DOMWrapper::setDOMWrapper(messageChannel,
>> >> > V8ClassIndex::MESSAGECHANNEL, obj.get());
>> >> > Those two MessagePort DOMWrappers don't seem to get freed, so they
>> >> > hold
>> >> > a
>> >> > reference to the underlying MessagePort impl objects.
>> >> > There are a few things I don't understand here:
>> >> > 1) I don't get why we need to explicitly keep a special reference to
>> >> > the
>> >> > MessagePorts here in internal fields - we don't seem to do that for
>> >> > other
>> >> > DOM Wrappers that have references like this (e.g. SharedWorker.port).
>> >>
>> >> There are a few corner-cases where we have to create additional links
>> >> in JS world to prevent things from being collected. Another example of
>> >> this are hidden dependencies that are used to keep event listeners on
>> >> DOM objects alive. IIRC, there is another part of this in
>> >> V8GCController dealing with message ports. Perhaps these two parts
>> >> conflict.
>> >>
>> >> > 2) Is there a good way to figure out why the garbage collector isn't
>> >> > collecting these references also, short of stepping through the V8 GC
>> >> > code?
>> >> > How do people typically track this stuff?
>> >> > Any pointers would be much appreciated.
>> >>
>> >> You could try using DevTools heap profiler. Alternatively in debug
>> >> mode there is TracePathToObject() (see
>> >>
>> >>
>> >> http://code.google.com/p/v8/source/browse/branches/bleeding_edge/src/heap.cc#3853).
>> >>
>> >> Is there a bug on this? I'm happy to have a look and fix it.
>> >>
>> >>
>> >> -- Vitaly
>> >
>> > --
>> > Chromium Developers mailing list: chromium-dev@googlegroups.com
>> > View archives, change email options, or unsubscribe:
>> > http://groups.google.com/group/chromium-dev
>
>

-- 
Chromium Developers mailing list: chromium-dev@googlegroups.com 
View archives, change email options, or unsubscribe: 
    http://groups.google.com/group/chromium-dev

Reply via email to