|
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 |