Re: Leaks in nsSessionStore.js?

Jeff Walden <[email protected]> Fri, 16 Nov 2007 00:34:58 -0500
Newsgroups gmane.comp.mozilla.performance
Message-ID <[email protected]>
Michael Vincent van Rantwijk, MultiZilla wrote:
> http://lxr.mozilla.org/seamonkey/source/browser/components/sessionstore/src/nsSessionStore.js#177 

Note the third parameter is |true|.  Looking at the IDL:

> If set to false, the nsIObserverService will hold a strong reference to |anObserver|. If set to true and |anObserver| supports the nsIWeakReference interface, a weak reference will be held. Otherwise an error will be returned.

The idea of a weak reference is that you don't increment the refcount -- you create an object which stores a pointer to whatever it is you want and return it instead.  Given that object, you call QueryReferent(iid) on it to get a strong reference, which only then increments the pointer.  The key to keeping this pointer from going stale on you is that when the actual object dies, its destructor will null out the pointer in the object you hold, so you can't use the dead pointer.  One other thing to note is that you only ever need one weak reference object no matter how many places might want one, so if you need a weak reference, you can be given the existing one.

The details are in <http://mxr.mozilla.org/mozilla/source/xpcom/glue/nsWeakReference.cpp>; I highly recommend spending half an hour or so reading it and figuring out how it works for the "aha!" moment you'll have.

Anyway, back to the question: the call says |this| implements nsISupportsWeakReference.  In C++ this would just be by inheriting from nsSupportsWeakReference, and in JS it's by having a QueryInterface which returns |this| when the provided IID is nsISupportsWeakReference.  Since we have the latter, XPConnect will make the wrapper implement nsISupportsWeakReference, and if that wrapper goes away because its refcount reaches zero, it'll clear the pointer stored in the one weak reference the prefbranch has, and since the prefbranch thus no longer has a pointer, no cycle occurs.

This is all a bit confusing, but I'm not sure there's really a good way to explain it -- you just need to stare at it for awhile until it makes sense.

Jeff