Closed Bug 372960 Opened 17 years ago Closed 17 years ago

Make XPConnect traverse more JS edges

Categories

(Core :: XPConnect, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED

People

(Reporter: peterv, Assigned: peterv)

Details

Attachments

(4 files, 2 obsolete files)

We're still missing edges for the cycle collection graph. I have a patch that makes the cycle collection code in nsXPConnect traverse more edges:

* for Script objects, traverse objects held in atoms in the atommap
* for Function objects, if FUN_INTERPRETED(fun) traverse objects held in atoms
  in the atommap of the script
* for an object's properties, walk scope property ids and any getters and setters

The patches combined seem to resolve the leaking of two windows when going to www.google.com/ig. I'll try them separately to see if any one is enough. This probably depends on the landing of bug 368773 to be really effective.
Attached patch v1 (obsolete) — Splinter Review
Both changes (atoms and getters/setters) are needed for this to be effective, so ended up combining everything into one patch. I can attach a diff -w, but it's not much more readable.
Attachment #257684 - Flags: review?(brendan)
Comment on attachment 257684 [details] [diff] [review]
v1

> {
>     jsval key;
> 
>-    if (atom->flags & ATOM_MARK)
>+    if (atom->flags & ATOM_MARK) {
>+        if (ATOM_IS_OBJECT(atom) && cx->runtime->gcThingCallback) {
>+            void *thing = ATOM_TO_OBJECT(atom);
>+            uint8 *flagp = js_GetGCThingFlags(thing);
>+            cx->runtime->gcThingCallback(thing, *flagp,
>+                                         cx->runtime->gcThingCallbackClosure);

Nit: house style avoids block locals, or at least (lately) puts a blank line between decls and stmts.

>-/* We avoid a large number of unnecessary calls by doing the flag check first */
> #define GC_MARK_ATOM(cx, atom)                                                \
>     JS_BEGIN_MACRO                                                            \
>-        if (!((atom)->flags & ATOM_MARK))                                     \
>-            js_MarkAtom(cx, atom);                                            \
>+        js_MarkAtom(cx, atom);                                                \
>     JS_END_MACRO

So there's no point in GC_MARK_ATOM; maybe we can remove it (more a reminder to igor and myself).

>+++ js/src/xpconnect/src/nsXPConnect.cpp	7 Mar 2007 15:07:14 -0000
>@@ -47,6 +47,9 @@
> #include "nsBaseHashtable.h"
> #include "nsHashKeys.h"
> #include "jsobj.h"
>+#include "jsscript.h"
>+#include "jsfun.h"
>+#include "jsatom.h"

Nit: alphabetize "js*.h" files as well as grouping them.

>+static void
>+TraverseJSScript(JSScript *script, nsCycleCollectionTraversalCallback &cb)
>+{
>+    JSAtomMap *map = &script->atomMap;
>+    uintN i, length = map->length;
>+    JSAtom **vector = map->vector;

Nit: xpconnect style would put a blank line here, I think.

>+            if(count > 0)
>+            {
>+                JS_snprintf(name, sizeof(name), "XPCNativeWrapper (%s)",
>+                            array[0]->GetNameString());
>+            }

Does XPConnect have a helper for this? Could you use nsIClassInfo to get the impl name instead of using the first interface's name?

>+            if (FUN_INTERPRETED(fun) && fun->u.i.script)
>+                TraverseJSScript(fun->u.i.script, cb);

Could just use FUN_SCRIPT(fun) in the if condition and the call.

>+    JSScope *scope = OBJ_SCOPE(obj);
>+    if (scope)
>+    {
>+        JSScopeProperty *sprop;
>+        for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent)
>+        {
>+            if (SCOPE_HAD_MIDDLE_DELETE(scope) &&
>+                !SCOPE_HAS_PROPERTY(scope, sprop))
>+                continue;
>+            jsid id = sprop->id;
>+            if (JSID_IS_OBJECT(id))
>+                cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
>+                                   JSID_TO_OBJECT(id));
>+            if (sprop->attrs & JSPROP_GETTER)
>+                cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
>+                                   JSVAL_TO_GCTHING((jsval)sprop->getter));
>+            if (sprop->attrs & JSPROP_SETTER)
>+                cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
>+                                   JSVAL_TO_GCTHING((jsval)sprop->setter));
>+        }
>+    }
>+

The scope may be shared among objects along the prototype chain, where the proto-children have not been mutated yet. Also, the sprops are shared in a runtime (heap) wide lexicographic tree. So I'm not sure that you want to note each as a child. To handle the first case (unmutated proto-kids), instead of

    if (scope)

you would test (and in xpconnect house style):

    if(scope && scope->object == obj)

To handle the sharing in rt->propertyTree of sprop nodes, I'm not sure what to do. It may not break Bacon-Rajan to treat each shared sprop as a child of every sharing object. Thoughts?

/be
Attachment #257684 - Flags: review?(brendan) → review-
Cc'ing Igor -- see comment 2.

/be
(In reply to comment #2)
> The scope may be shared among objects along the prototype chain, where the
> proto-children have not been mutated yet. Also, the sprops are shared in a
> runtime (heap) wide lexicographic tree. So I'm not sure that you want to note
> each as a child. To handle the first case (unmutated proto-kids), instead of
> 
>     if (scope)
> 
> you would test (and in xpconnect house style):
> 
>     if(scope && scope->object == obj)
> 
> To handle the sharing in rt->propertyTree of sprop nodes, I'm not sure what to
> do. It may not break Bacon-Rajan to treat each shared sprop as a child of every
> sharing object. Thoughts?

I don't think either is a problem, afaict js_Mark just marks all sprops for the object, so the gcThinkCallback is called for all the sprops of an object's scope, regardless of sharing. The traversal code needs to mimic the marking that happens, so I think the patch should be ok on this point. Make sense?
I'll take care of the other comments.
Yes, if we model everything in JS's heap as ref-counted, then the high degree of sharing via the property tree means high modeled ref-counts for objects owned by the sprops (id, getter, setter).

/be
I see some weird crashes, I think I should be checking OBJ_IS_NATIVE(obj) before traversing the scope.
(In reply to comment #6)
> I see some weird crashes, I think I should be checking OBJ_IS_NATIVE(obj)
> before traversing the scope.

Quite -- sorry I missed that.

/be
Attached patch v1.1 (obsolete) — Splinter Review
PConnect doesn't really have a helper to print the classname, XPCWrappedNative::ToString generates strings that are too long to be useful for the graphs.
Attachment #257684 - Attachment is obsolete: true
Attachment #257888 - Flags: review?(brendan)
Comment on attachment 257888 [details] [diff] [review]
v1.1

Looks good -- only nit is that the xpconnect patches mix Foo* and Bar * pointer style (cuddling the * with the type sometimes, with the declarator name other times).

/be
Attached patch v1.2Splinter Review
Yeah, I'm definitely having trouble using the correct coding styles in all these modules, sorry about that :-/.
Brendan: did you mean to mark this r+? I'm also going to need help checking in the mozilla/js/src part of this patch.
Attachment #257888 - Attachment is obsolete: true
Attachment #257947 - Flags: superreview?(jst)
Attachment #257947 - Flags: review?(brendan)
Attachment #257888 - Flags: review?(brendan)
Comment on attachment 257947 [details] [diff] [review]
v1.2

r=me, and I'm making you a member of the js partition.

/be
Attachment #257947 - Flags: review?(brendan) → review+
Comment on attachment 257947 [details] [diff] [review]
v1.2

sr=jst
Attachment #257947 - Flags: superreview?(jst) → superreview+
Whiteboard: [needs checkin]
Checked this in, but had to back out the nsXPConnect chunk because it turned the OS X test tinderbox orange, toolkit/themes/pinstripe/tests/test_bug371080.xul started timing out.
Status: NEW → ASSIGNED
Whiteboard: [needs checkin]
###!!! ASSERTION: Fault in cycle collector: traversed refs exceed refcount (ptr: 1c92080)

I learned from the mp3 of the cycle collector conference call that is bad news. This is with peter's patch applied, but I think his patch might just make it easier to trigger. I had to back out my parser tests because they triggered this same test failure, probably by creating a lot of churn through data: iframes.
Attached file live test case
this will assert and eventually crash the trunk if you reload enough, at least with all of attachment 257947 [details] [diff] [review] applied.
I should add that I am running linux at the moment. The test cases will fail if you are on linux or windows. This is turning the mac tinderbox orange because it is the only one that runs pinstripe tests--if we had an equivalent test on linux or windows, we would probably see it there too.
I just saw something similar occur on the windows unit test box. The error message in the js console was that script="foo" was or contained a syntax error, even though this test has been run hundreds of times before.

see the <a href="http://tinderbox.mozilla.org/showlog.cgi?log=Firefox/1173720974.1173723925.11618.gz&fulltext=1">full log</a> for the freeze, though details there aren't really useful.
robcee has the mac unit test box running debug builds, so perhaps we can reland this and observe?
AFAICS the last patch is incomplete. It does not traverse XML objects which store their XML children/parents/qnames in their private structures. Since one can store, for example, in qname or in XML functions an arbitrary object including a DOM wrapper, the traversal code must deal with them.
Maybe we should really separate GC marking and code traversal in SpderMonkey? 

I.e. instead of traversal and marking at the same time the code would borrow a cycle collector idea about NoteChild and replace the marking semantic in various Mark function via a traversal one. That is, instead of implementing JS GC marking the GC-related things should provide a way to enumerate their GC-controllable children. 

Then js_GC would use the traversal code to implement the marking phase while the cycle collector would bridge it to make JS entities to participate in the cycle collection. 

I think it should be even possible without breaking public API compatibility as the arg argument of JS_MarkGCThing/JSMarkOp can be used to pass the child notification callback.
I filed bug 375270 about making the marking phase of JS GC just one particular user of the traversal code.
The patch has been completely landed, but we did see some tinderbox orange. Both sayrer and I tried to add debugging code to figure out why the tinderbox goes orange, but adding debugging code fixed the orange :-/.
Any thoughts yet on whether the orange was due to actual problems or some weirdness with the testing system? Are we ready to close this bug yet?
I'm going to mark this fixed. I don't think we're still seeing the orange that we saw when this landed. Let's file a new bug if it returns.
Status: ASSIGNED → RESOLVED
Closed: 17 years ago
Resolution: --- → FIXED
Flags: in-testsuite?
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: