Closed Bug 102332 Opened 23 years ago Closed 23 years ago

9K leaks on tinderbox: cycle relating to HTTP handler

Categories

(Core :: Networking: HTTP, defect, P2)

x86
Linux
defect

Tracking

()

RESOLVED FIXED
mozilla0.9.5

People

(Reporter: dbaron, Assigned: darin.moz)

Details

(Keywords: memory-leak, Whiteboard: [tind-mlk])

Attachments

(2 files, 1 obsolete file)

The changes in bug 52510 (checked in 2001-09-27 14:26 PDT) caused the tinderbox leak stats to increase from around 560B (the fix to get them back down to 560B went in very near the same time) to oscillating around 9K.
Keywords: mlk
Whiteboard: [tind-mlk]
So, here's the deal, at least partly, I think: The nsHttpHandler registers, in its Init method, as an observer on the pref branch, so that the pref branch owns 4 references to the nsHttpHandler. The nsHttpHandler also holds on to this pref branch as mPrefs. (The pref branch is also owned by the pref service, which is leaked by something else in a bigger cycle..) What's the general rule: should users of the pref service own it, or should they register as listeners? Doing both is prone to leaks -- we've seen this before (e.g., with caps).
w/ out thinking much about it, they should just register as listeners, and not cache the service.
When the observe method is called the associated prefBranch is passed in. This would, in theory, allow the consumer to not maintain a reference to the prefBranch. In practice however most consumers that I have seen also keep the prefBranch around for other preference accesses. The other solution (as in caps) would be to hold a weak reference to the prefBranch. As far as a general rule... That's a tough one. In general, I'd say most consumers just check the preference before using it. There is, however, a certain amount of overhead that goes with this. I would think that it would be more efficient to locally store your own copies of the current state and then listen for changes. This will, unfortunately, add bloat. As a side note, the observer support was added in the nsIPrefBranchInternal interface. If we want to suggest the "general rule" is to observe changes, that support should probably be brought back out to the nsIPrefBranch interface so it is more easily obtainable.
I think a general rule is bad, but there are three techniques we should advertise: 1) keep a weak reference to the branch, check it every time we need the value Best for: whenever we don't need to "react" to a preference changing - i.e. no visual or internal state is transformed because this value changed. An example might be the browser's homepage - when a user changes that pref, it only affects the opening of a new window, so there's no need to "react" to someone changing that preference. 2) keep a callback object which "reacts" to the pref changing. An example of this might be the Print button pref. When the user turns the print button on or off through the preferences, we need all the browser windows to update themselves immediately. 3) Always check, but be lazy if there is no urgency for reacting to the value. An example of this might be the changing of cache size - when the cache size is changed to be smaller, we need to purge disk or memory of some cached pages. However, it may be that the cache doesn't actually get purged until the next page gets read in. In this case, it seems like the simplest solution would be to hold a weak reference to the pref branch.
The reason I suggested a general rule might be needed is that some object A in one component registers as a pref observer, and some object B in another component owns the pref branch or related object, then if A (directly or indirectly) owns a reference to B we end up with a cycle (prefs->A->B->prefs). This would mean practically anytime anyone introduces either type of ownership model they risk introducing a cycle somewhere. However, what I was forgetting is that all of the prefs->A references would either be broken by something other than a destructor or would just be one-time leaks (A would be a service or some other object that lasts the lifetime of the application).
it should be easy enough to make nsHttpHandler just "cache" the prefBranch for the duration of the Observe callback, since it is not really needed (repeatedly) outside of this callback. i would also like it if we added some comments to nsIPrefBranchInternal that just warn consumers to think very carefully about potential cycles. -> moz 0.9.5
Status: NEW → ASSIGNED
Priority: -- → P2
Target Milestone: --- → mozilla0.9.5
Sure I'm rev'ing the documentation for API freeze anyway. Easily done.
actually, I just remembered that in the pref listener's Observe() method, the nsISupports parameter can actually be QI'ed to the pref service, and to the root pref branch...that might make it a little simpler.
Actually, 1) isn't entirely correct. We should really update the tooltip on the home button ;-)
And, actually, ;) as I noted above... it's the actual nsIPrefBranch that's passed in the nsISupports parameter, not the nsIPrefService. So you can just QI it into the branch that you created, and use it... no messing around required.
Whiteboard: [tind-mlk] → [tind-mlk] [patch-in-hand]
Attached patch v1.0 fixes leaks (obsolete) — Splinter Review
Comment on attachment 51613 [details] [diff] [review] v1.0 fixes leaks this patch fixes the leaks, and also makes http observe changes to the pref (now appropriately renamed to network.http.use-cache), which enables/disables http caching.
Comment on attachment 51613 [details] [diff] [review] v1.0 fixes leaks sr=alecf
Attachment #51613 - Flags: superreview+
Comment on attachment 51613 [details] [diff] [review] v1.0 fixes leaks r=gagan
Attachment #51613 - Flags: review+
fixed-on-trunk
Status: ASSIGNED → RESOLVED
Closed: 23 years ago
Resolution: --- → FIXED
Whiteboard: [tind-mlk] [patch-in-hand] → [tind-mlk]
Hrm. The leak numbers on tinderbox didn't go down.
reopening... i think i have an idea of what the problem actually is: pref service references the http handler as an observer, but the http handler only removes itself as an observer once it is deleted. i suspect that the observer entry in libpref actually references the pref branch, so therein lies the cycle.
Status: RESOLVED → REOPENED
Resolution: FIXED → ---
this wasn't a problem when HTTP used the old nsIPref interface since the callback code would only reference a static function pointer and the http handler via a void* reference.
much like it was a solution for nsObserverList, i think the solution for this bug is to make the pref branch hold only weak references to the pref observers (if the pref observer supports weak reference). see nsObserverList.cpp:218
the reason for this is that it otherwise makes every pref observer add extra code to avoid this kind of cycle. with weak references it makes it pref observer code much simpler.
Attachment #51613 - Attachment is obsolete: true
we've discussed this before, and we decided against it. I think part of the issue was that some people had helper-objects whose only reference was libpref and using weak references would make the object get deleted immediately many people objected to the way nsObserverList tries to be "smart" and maintain a weak ref if possible and fallback to a strong ref, because the caller had no control about how it was maintained (i.e. if you implemented weakref for another unrelated reason, you were forced to be held as a weak reference by nsObserverList) Maybe before this interface freezes we ought to change it to: addObserver(in string aDomain, in nsIObserver observer, in boolean holdWeak); so that the caller can decide?
so, i verified using find-leakers.pl that this patch actually fixes the leak. it makes the pref branch code QI for nsISupportsWeakReference in AddObserver... and, if aObserver implements nsISupportsWeakReference, then the pref branch will only weak reference the observer. as i mentioned before, this is what the observer service does... and i think it is the right solution here as well. details: to avoid excessive QI calls, i added a member to PrefCallbackData to record whether or not the stored pObserver is a weak reference or not. then in NotifyObserver, i just NS_STATIC_CAST to the appropriate interface instead of QI'ing again. basically, i've tried to minimize any extra runtime overhead introduced for the non-weak-reference case. however, this means increasing the size of PrefCallbackData by 50%... i think this is the right trade-off.
Status: REOPENED → ASSIGNED
alecf: missed your comments before posting this patch, and after discussing this some with bnesse, i suppose i see why this is not a feasible solution at this time. so, i see two alternate solutions: 1- use a proxy container object for the http handler which holds only a weak reference back to the http handler. 2- make the http handler observe xpcom-shutdown and cleanup then.
again, i verified this patch using find-leakers.pl, and it seems to do the trick.
Comment on attachment 51746 [details] [diff] [review] patch: solves problem using option 2 (observe xpcom-shutdown) awesome. sr=alecf
Attachment #51746 - Flags: superreview+
Attachment #51746 - Flags: review+
Comment on attachment 51746 [details] [diff] [review] patch: solves problem using option 2 (observe xpcom-shutdown) got an r=dougt via AIM
ok.. fixed-on-trunk
Status: ASSIGNED → RESOLVED
Closed: 23 years ago23 years ago
Resolution: --- → FIXED
pls check this into the branch - PDT+, is 52510 is checkin.
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: