xpcom/ds/nsObserverService.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:e877a75aae4b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "prlog.h"
7 #include "nsAutoPtr.h"
8 #include "nsIObserverService.h"
9 #include "nsIObserver.h"
10 #include "nsObserverService.h"
11 #include "nsObserverList.h"
12 #include "nsThreadUtils.h"
13 #include "nsEnumeratorUtils.h"
14 #include "mozilla/net/NeckoCommon.h"
15 #include "mozilla/Services.h"
16
17 #define NOTIFY_GLOBAL_OBSERVERS
18
19 #if defined(PR_LOGGING)
20 // Log module for nsObserverService logging...
21 //
22 // To enable logging (see prlog.h for full details):
23 //
24 // set NSPR_LOG_MODULES=ObserverService:5
25 // set NSPR_LOG_FILE=nspr.log
26 //
27 // this enables PR_LOG_DEBUG level information and places all output in
28 // the file nspr.log
29 static PRLogModuleInfo*
30 GetObserverServiceLog()
31 {
32 static PRLogModuleInfo *sLog;
33 if (!sLog)
34 sLog = PR_NewLogModule("ObserverService");
35 return sLog;
36 }
37 #define LOG(x) PR_LOG(GetObserverServiceLog(), PR_LOG_DEBUG, x)
38 #else
39 #define LOG(x)
40 #endif /* PR_LOGGING */
41
42 namespace mozilla {
43
44 struct SuspectObserver {
45 SuspectObserver(const char* aTopic, size_t aReferentCount)
46 : topic(aTopic), referentCount(aReferentCount) {}
47 const char* topic;
48 size_t referentCount;
49 };
50
51 struct ObserverServiceReferentCount {
52 ObserverServiceReferentCount()
53 : numStrong(0), numWeakAlive(0), numWeakDead(0) {}
54 size_t numStrong;
55 size_t numWeakAlive;
56 size_t numWeakDead;
57 nsTArray<SuspectObserver> suspectObservers;
58 };
59
60 } // namespace mozilla
61
62 using namespace mozilla;
63
64 PLDHashOperator
65 nsObserverService::CountReferents(nsObserverList* aObserverList,
66 void* aClosure)
67 {
68 if (!aObserverList) {
69 return PL_DHASH_NEXT;
70 }
71
72 ObserverServiceReferentCount* referentCount =
73 static_cast<ObserverServiceReferentCount*>(aClosure);
74
75 size_t numStrong = 0;
76 size_t numWeakAlive = 0;
77 size_t numWeakDead = 0;
78
79 nsTArray<ObserverRef>& observers = aObserverList->mObservers;
80 for (uint32_t i = 0; i < observers.Length(); i++) {
81 if (observers[i].isWeakRef) {
82 nsCOMPtr<nsIObserver> observerRef(
83 do_QueryReferent(observers[i].asWeak()));
84 if (observerRef) {
85 numWeakAlive++;
86 } else {
87 numWeakDead++;
88 }
89 } else {
90 numStrong++;
91 }
92 }
93
94 referentCount->numStrong += numStrong;
95 referentCount->numWeakAlive += numWeakAlive;
96 referentCount->numWeakDead += numWeakDead;
97
98 // Keep track of topics that have a suspiciously large number
99 // of referents (symptom of leaks).
100 size_t total = numStrong + numWeakAlive + numWeakDead;
101 if (total > kSuspectReferentCount) {
102 SuspectObserver suspect(aObserverList->GetKey(), total);
103 referentCount->suspectObservers.AppendElement(suspect);
104 }
105
106 return PL_DHASH_NEXT;
107 }
108
109 NS_IMETHODIMP
110 nsObserverService::CollectReports(nsIHandleReportCallback* aHandleReport,
111 nsISupports* aData)
112 {
113 ObserverServiceReferentCount referentCount;
114 mObserverTopicTable.EnumerateEntries(CountReferents, &referentCount);
115
116 nsresult rv;
117 for (uint32_t i = 0; i < referentCount.suspectObservers.Length(); i++) {
118 SuspectObserver& suspect = referentCount.suspectObservers[i];
119 nsPrintfCString suspectPath("observer-service-suspect/"
120 "referent(topic=%s)",
121 suspect.topic);
122 rv = aHandleReport->Callback(/* process */ EmptyCString(),
123 suspectPath, KIND_OTHER, UNITS_COUNT, suspect.referentCount,
124 NS_LITERAL_CSTRING("A topic with a suspiciously large number of "
125 "referents. This may be symptomatic of a leak "
126 "if the number of referents is high with "
127 "respect to the number of windows."),
128 aData);
129
130 if (NS_WARN_IF(NS_FAILED(rv)))
131 return rv;
132 }
133
134 rv = aHandleReport->Callback(/* process */ EmptyCString(),
135 NS_LITERAL_CSTRING("observer-service/referent/strong"),
136 KIND_OTHER, UNITS_COUNT, referentCount.numStrong,
137 NS_LITERAL_CSTRING("The number of strong references held by the "
138 "observer service."),
139 aData);
140
141 if (NS_WARN_IF(NS_FAILED(rv)))
142 return rv;
143
144 rv = aHandleReport->Callback(/* process */ EmptyCString(),
145 NS_LITERAL_CSTRING("observer-service/referent/weak/alive"),
146 KIND_OTHER, UNITS_COUNT, referentCount.numWeakAlive,
147 NS_LITERAL_CSTRING("The number of weak references held by the "
148 "observer service that are still alive."),
149 aData);
150
151 if (NS_WARN_IF(NS_FAILED(rv)))
152 return rv;
153
154 rv = aHandleReport->Callback(/* process */ EmptyCString(),
155 NS_LITERAL_CSTRING("observer-service/referent/weak/dead"),
156 KIND_OTHER, UNITS_COUNT, referentCount.numWeakDead,
157 NS_LITERAL_CSTRING("The number of weak references held by the "
158 "observer service that are dead."),
159 aData);
160
161 if (NS_WARN_IF(NS_FAILED(rv)))
162 return rv;
163
164 return NS_OK;
165 }
166
167 ////////////////////////////////////////////////////////////////////////////////
168 // nsObserverService Implementation
169
170
171 NS_IMPL_ISUPPORTS(
172 nsObserverService,
173 nsIObserverService,
174 nsObserverService,
175 nsIMemoryReporter)
176
177 nsObserverService::nsObserverService() :
178 mShuttingDown(false)
179 {
180 }
181
182 nsObserverService::~nsObserverService(void)
183 {
184 Shutdown();
185 }
186
187 void
188 nsObserverService::RegisterReporter()
189 {
190 RegisterWeakMemoryReporter(this);
191 }
192
193 void
194 nsObserverService::Shutdown()
195 {
196 UnregisterWeakMemoryReporter(this);
197
198 mShuttingDown = true;
199
200 mObserverTopicTable.Clear();
201 }
202
203 nsresult
204 nsObserverService::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
205 {
206 LOG(("nsObserverService::Create()"));
207
208 nsRefPtr<nsObserverService> os = new nsObserverService();
209
210 if (!os)
211 return NS_ERROR_OUT_OF_MEMORY;
212
213 // The memory reporter can not be immediately registered here because
214 // the nsMemoryReporterManager may attempt to get the nsObserverService
215 // during initialization, causing a recursive GetService.
216 nsRefPtr<nsRunnableMethod<nsObserverService> > registerRunnable =
217 NS_NewRunnableMethod(os, &nsObserverService::RegisterReporter);
218 NS_DispatchToCurrentThread(registerRunnable);
219
220 return os->QueryInterface(aIID, aInstancePtr);
221 }
222
223 #define NS_ENSURE_VALIDCALL \
224 if (!NS_IsMainThread()) { \
225 MOZ_CRASH("Using observer service off the main thread!"); \
226 return NS_ERROR_UNEXPECTED; \
227 } \
228 if (mShuttingDown) { \
229 NS_ERROR("Using observer service after XPCOM shutdown!"); \
230 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; \
231 }
232
233 NS_IMETHODIMP
234 nsObserverService::AddObserver(nsIObserver* anObserver, const char* aTopic,
235 bool ownsWeak)
236 {
237 LOG(("nsObserverService::AddObserver(%p: %s)",
238 (void*) anObserver, aTopic));
239
240 NS_ENSURE_VALIDCALL
241 if (NS_WARN_IF(!anObserver) || NS_WARN_IF(!aTopic))
242 return NS_ERROR_INVALID_ARG;
243
244 if (mozilla::net::IsNeckoChild() && !strncmp(aTopic, "http-on-", 8)) {
245 return NS_ERROR_NOT_IMPLEMENTED;
246 }
247
248 nsObserverList *observerList = mObserverTopicTable.PutEntry(aTopic);
249 if (!observerList)
250 return NS_ERROR_OUT_OF_MEMORY;
251
252 return observerList->AddObserver(anObserver, ownsWeak);
253 }
254
255 NS_IMETHODIMP
256 nsObserverService::RemoveObserver(nsIObserver* anObserver, const char* aTopic)
257 {
258 LOG(("nsObserverService::RemoveObserver(%p: %s)",
259 (void*) anObserver, aTopic));
260 NS_ENSURE_VALIDCALL
261 if (NS_WARN_IF(!anObserver) || NS_WARN_IF(!aTopic))
262 return NS_ERROR_INVALID_ARG;
263
264 nsObserverList *observerList = mObserverTopicTable.GetEntry(aTopic);
265 if (!observerList)
266 return NS_ERROR_FAILURE;
267
268 /* This death grip is to protect against stupid consumers who call
269 RemoveObserver from their Destructor, see bug 485834/bug 325392. */
270 nsCOMPtr<nsIObserver> kungFuDeathGrip(anObserver);
271 return observerList->RemoveObserver(anObserver);
272 }
273
274 NS_IMETHODIMP
275 nsObserverService::EnumerateObservers(const char* aTopic,
276 nsISimpleEnumerator** anEnumerator)
277 {
278 NS_ENSURE_VALIDCALL
279 if (NS_WARN_IF(!anEnumerator) || NS_WARN_IF(!aTopic))
280 return NS_ERROR_INVALID_ARG;
281
282 nsObserverList *observerList = mObserverTopicTable.GetEntry(aTopic);
283 if (!observerList)
284 return NS_NewEmptyEnumerator(anEnumerator);
285
286 return observerList->GetObserverList(anEnumerator);
287 }
288
289 // Enumerate observers of aTopic and call Observe on each.
290 NS_IMETHODIMP nsObserverService::NotifyObservers(nsISupports *aSubject,
291 const char *aTopic,
292 const char16_t *someData)
293 {
294 LOG(("nsObserverService::NotifyObservers(%s)", aTopic));
295
296 NS_ENSURE_VALIDCALL
297 if (NS_WARN_IF(!aTopic))
298 return NS_ERROR_INVALID_ARG;
299
300 nsObserverList *observerList = mObserverTopicTable.GetEntry(aTopic);
301 if (observerList)
302 observerList->NotifyObservers(aSubject, aTopic, someData);
303
304 #ifdef NOTIFY_GLOBAL_OBSERVERS
305 observerList = mObserverTopicTable.GetEntry("*");
306 if (observerList)
307 observerList->NotifyObservers(aSubject, aTopic, someData);
308 #endif
309
310 return NS_OK;
311 }
312
313 static PLDHashOperator
314 UnmarkGrayObserverEntry(nsObserverList* aObserverList, void* aClosure)
315 {
316 if (aObserverList) {
317 aObserverList->UnmarkGrayStrongObservers();
318 }
319 return PL_DHASH_NEXT;
320 }
321
322 NS_IMETHODIMP
323 nsObserverService::UnmarkGrayStrongObservers()
324 {
325 NS_ENSURE_VALIDCALL
326
327 mObserverTopicTable.EnumerateEntries(UnmarkGrayObserverEntry, nullptr);
328
329 return NS_OK;
330 }
331

mercurial