VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/base/nsExceptionService.cpp@ 78394

Last change on this file since 78394 was 65440, checked in by vboxsync, 8 years ago

XPCOM: Previous change broke the shutdown detection in nsExceptionService, causing races with creating new exception manager instances when XPCOM shutdown has begun. The race happened when XPCOM shutdown was destroying all object instances for the known components. With "enough luck" nsExceptionService::GetCurrentExceptionManager created a new manager which by the time the AddRef call at the end was reached would've been already freed (which happens regardless of refcounts for obvious reasons).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.1 KB
Line 
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is mozilla.org code.
16 *
17 * The Initial Developer of the Original Code is
18 * ActiveState Tool Corp..
19 * Portions created by the Initial Developer are Copyright (C) 2001
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 * Mark Hammond <[email protected]>
24 *
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
36 *
37 * ***** END LICENSE BLOCK ***** */
38
39#include "nsISupports.h"
40#include "nsExceptionService.h"
41#include "nsIServiceManager.h"
42#include "nsCOMPtr.h"
43#include "prthread.h"
44#include "prlock.h"
45static const PRUintn BAD_TLS_INDEX = (PRUintn) -1;
46
47#define CHECK_SERVICE_USE_OK() if (tlsIndex == BAD_TLS_INDEX) return NS_ERROR_NOT_INITIALIZED
48#define CHECK_MANAGER_USE_OK() if (!mService || nsExceptionService::tlsIndex == BAD_TLS_INDEX) return NS_ERROR_NOT_INITIALIZED
49
50// A key for our registered module providers hashtable
51class nsProviderKey : public nsHashKey {
52protected:
53 PRUint32 mKey;
54public:
55 nsProviderKey(PRUint32 key) : mKey(key) {}
56 PRUint32 HashCode(void) const {
57 return mKey;
58 }
59 PRBool Equals(const nsHashKey *aKey) const {
60 return mKey == ((const nsProviderKey *) aKey)->mKey;
61 }
62 nsHashKey *Clone() const {
63 return new nsProviderKey(mKey);
64 }
65 PRUint32 GetValue() { return mKey; }
66};
67
68/** Exception Manager definition **/
69class nsExceptionManager : public nsIExceptionManager
70{
71public:
72 NS_DECL_ISUPPORTS
73 NS_DECL_NSIEXCEPTIONMANAGER
74
75 nsExceptionManager(nsExceptionService *svc);
76 /* additional members */
77 nsCOMPtr<nsIException> mCurrentException;
78 nsExceptionManager *mNextThread; // not ref-counted.
79 nsExceptionService *mService; // not ref-counted
80#ifdef NS_DEBUG
81 static PRInt32 totalInstances;
82#endif
83
84#ifdef NS_DEBUG
85 inline nsrefcnt ReleaseQuiet() {
86 // shut up NS_ASSERT_OWNINGTHREAD (see explanation below)
87 nsAutoOwningThread old = _mOwningThread;
88 _mOwningThread = nsAutoOwningThread();
89 nsrefcnt ref = Release();
90 NS_ASSERTION(ref == 0, "the object is still referenced by other threads while it shouldn't");
91 if (ref != 0)
92 _mOwningThread = old;
93 return ref;
94 }
95#else
96 inline nsrefcnt ReleaseQuiet(void) { return Release(); }
97#endif
98
99private:
100 ~nsExceptionManager();
101};
102
103
104#ifdef NS_DEBUG
105PRInt32 nsExceptionManager::totalInstances = 0;
106#endif
107
108// Note: the nsExceptionManager object is single threaded - the exception
109// service itself ensures one per thread. However, there are two methods that
110// may be called on foreign threads: DropAllThreads (called on the thread
111// shutting down xpcom) and ThreadDestruct (called after xpcom destroyed the
112// internal thread struct, so that PR_GetCurrentThread() will create a new one
113// from scratch which will obviously not match the old one stored in the
114// instance on creation). In both cases, there should be no other threads
115// holding objects (i.e. it's thread-safe to call them), but
116// NS_CheckThreadSafe() assertions will still happen and yell in the debug
117// build. Since it is quite annoying, we use a special ReleaseQuiet() method
118// in DoDropThread() to shut them up.
119NS_IMPL_THREADSAFE_ISUPPORTS1(nsExceptionManager, nsIExceptionManager)
120
121nsExceptionManager::nsExceptionManager(nsExceptionService *svc) :
122 mNextThread(nsnull),
123 mService(svc)
124{
125 /* member initializers and constructor code */
126#ifdef NS_DEBUG
127 PR_AtomicIncrement(&totalInstances);
128#endif
129}
130
131nsExceptionManager::~nsExceptionManager()
132{
133 /* destructor code */
134#ifdef NS_DEBUG
135 PR_AtomicDecrement(&totalInstances);
136#endif // NS_DEBUG
137}
138
139/* void setCurrentException (in nsIException error); */
140NS_IMETHODIMP nsExceptionManager::SetCurrentException(nsIException *error)
141{
142 CHECK_MANAGER_USE_OK();
143 mCurrentException = error;
144 return NS_OK;
145}
146
147/* nsIException getCurrentException (); */
148NS_IMETHODIMP nsExceptionManager::GetCurrentException(nsIException **_retval)
149{
150 CHECK_MANAGER_USE_OK();
151 *_retval = mCurrentException;
152 NS_IF_ADDREF(*_retval);
153 return NS_OK;
154}
155
156/* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
157NS_IMETHODIMP nsExceptionManager::GetExceptionFromProvider(nsresult rc, nsIException * defaultException, nsIException **_retval)
158{
159 CHECK_MANAGER_USE_OK();
160 // Just delegate back to the service with the provider map.
161 return mService->GetExceptionFromProvider(rc, defaultException, _retval);
162}
163
164/* The Exception Service */
165
166PRUintn nsExceptionService::tlsIndex = BAD_TLS_INDEX;
167PRLock *nsExceptionService::lock = nsnull;
168nsExceptionManager *nsExceptionService::firstThread = nsnull;
169
170#ifdef NS_DEBUG
171PRInt32 nsExceptionService::totalInstances = 0;
172#endif
173
174NS_IMPL_THREADSAFE_ISUPPORTS2(nsExceptionService, nsIExceptionService, nsIObserver)
175
176nsExceptionService::nsExceptionService()
177 : mProviders(4, PR_TRUE) /* small, thread-safe hashtable */
178{
179#ifdef NS_DEBUG
180 if (PR_AtomicIncrement(&totalInstances)!=1) {
181 NS_ERROR("The nsExceptionService is a singleton!");
182 }
183#endif
184 /* member initializers and constructor code */
185 if (tlsIndex == BAD_TLS_INDEX) {
186 PRStatus status;
187 status = PR_NewThreadPrivateIndex( &tlsIndex, ThreadDestruct );
188 NS_WARN_IF_FALSE(status==0, "ScriptErrorService could not allocate TLS storage.");
189 }
190 lock = PR_NewLock();
191 NS_WARN_IF_FALSE(lock, "Error allocating ExceptionService lock");
192
193 // observe XPCOM shutdown.
194 nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1");
195 NS_WARN_IF_FALSE(observerService, "Could not get observer service!");
196 if (observerService)
197 observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
198}
199
200nsExceptionService::~nsExceptionService()
201{
202 Shutdown();
203 if (lock) {
204 PRLock *tmp = lock;
205 lock = nsnull;
206 PR_DestroyLock(tmp);
207 }
208 /* destructor code */
209#ifdef NS_DEBUG
210 PR_AtomicDecrement(&totalInstances);
211#endif
212}
213
214/*static*/
215void nsExceptionService::ThreadDestruct( void *data )
216{
217 if (!lock) {
218 NS_WARNING("nsExceptionService ignoring thread destruction after shutdown");
219 return;
220 }
221 DropThread( (nsExceptionManager *)data );
222}
223
224
225void nsExceptionService::Shutdown()
226{
227 PRUintn tmp = tlsIndex;
228 tlsIndex = BAD_TLS_INDEX;
229 PR_SetThreadPrivate(tmp, nsnull);
230 mProviders.Reset();
231 if (lock) {
232 DropAllThreads();
233 }
234}
235
236/* void setCurrentException (in nsIException error); */
237NS_IMETHODIMP nsExceptionService::SetCurrentException(nsIException *error)
238{
239 CHECK_SERVICE_USE_OK();
240 nsCOMPtr<nsIExceptionManager> sm;
241 nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm));
242 if (NS_FAILED(nr))
243 return nr;
244 return sm->SetCurrentException(error);
245}
246
247/* nsIException getCurrentException (); */
248NS_IMETHODIMP nsExceptionService::GetCurrentException(nsIException **_retval)
249{
250 CHECK_SERVICE_USE_OK();
251 nsCOMPtr<nsIExceptionManager> sm;
252 nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm));
253 if (NS_FAILED(nr))
254 return nr;
255 return sm->GetCurrentException(_retval);
256}
257
258/* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
259NS_IMETHODIMP nsExceptionService::GetExceptionFromProvider(nsresult rc,
260 nsIException * defaultException, nsIException **_retval)
261{
262 CHECK_SERVICE_USE_OK();
263 return DoGetExceptionFromProvider(rc, defaultException, _retval);
264}
265
266/* readonly attribute nsIExceptionManager currentExceptionManager; */
267NS_IMETHODIMP nsExceptionService::GetCurrentExceptionManager(nsIExceptionManager * *aCurrentScriptManager)
268{
269 CHECK_SERVICE_USE_OK();
270 nsExceptionManager *mgr = (nsExceptionManager *)PR_GetThreadPrivate(tlsIndex);
271 if (mgr == nsnull) {
272 // Stick the new exception object in with no reference count.
273 mgr = new nsExceptionManager(this);
274 if (mgr == nsnull)
275 return NS_ERROR_OUT_OF_MEMORY;
276 PR_SetThreadPrivate(tlsIndex, mgr);
277 // The reference count is held in the thread-list
278 AddThread(mgr);
279 }
280 *aCurrentScriptManager = mgr;
281 NS_ADDREF(*aCurrentScriptManager);
282 return NS_OK;
283}
284
285/* void registerErrorProvider (in nsIExceptionProvider provider, in PRUint32 moduleCode); */
286NS_IMETHODIMP nsExceptionService::RegisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule)
287{
288 CHECK_SERVICE_USE_OK();
289
290 nsProviderKey key(errorModule);
291 if (mProviders.Put(&key, provider)) {
292 NS_WARNING("Registration of exception provider overwrote another provider with the same module code!");
293 }
294 return NS_OK;
295}
296
297/* void unregisterErrorProvider (in nsIExceptionProvider provider, in PRUint32 errorModule); */
298NS_IMETHODIMP nsExceptionService::UnregisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule)
299{
300 CHECK_SERVICE_USE_OK();
301 nsProviderKey key(errorModule);
302 if (!mProviders.Remove(&key)) {
303 NS_WARNING("Attempt to unregister an unregistered exception provider!");
304 return NS_ERROR_UNEXPECTED;
305 }
306 return NS_OK;
307}
308
309// nsIObserver
310NS_IMETHODIMP nsExceptionService::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
311{
312 Shutdown();
313 return NS_OK;
314}
315
316nsresult
317nsExceptionService::DoGetExceptionFromProvider(nsresult errCode,
318 nsIException * defaultException,
319 nsIException **_exc)
320{
321 // Check for an existing exception
322 nsresult nr = GetCurrentException(_exc);
323 if (NS_SUCCEEDED(nr) && *_exc) {
324 (*_exc)->GetResult(&nr);
325 // If it matches our result then use it
326 if (nr == errCode)
327 return NS_OK;
328 NS_RELEASE(*_exc);
329 }
330 nsProviderKey key(NS_ERROR_GET_MODULE(errCode));
331 nsCOMPtr<nsIExceptionProvider> provider =
332 dont_AddRef((nsIExceptionProvider *)mProviders.Get(&key));
333
334 // No provider so we'll return the default exception
335 if (!provider) {
336 *_exc = defaultException;
337 NS_IF_ADDREF(*_exc);
338 return NS_OK;
339 }
340
341 return provider->GetException(errCode, defaultException, _exc);
342}
343
344// thread management
345/*static*/ void nsExceptionService::AddThread(nsExceptionManager *thread)
346{
347 PR_Lock(lock);
348 thread->mNextThread = firstThread;
349 firstThread = thread;
350 NS_ADDREF(thread);
351 PR_Unlock(lock);
352}
353
354/*static*/ void nsExceptionService::DoDropThread(nsExceptionManager *thread)
355{
356 nsExceptionManager **emp = &firstThread;
357 while (*emp != thread) {
358 if (!*emp)
359 {
360 NS_WARNING("Could not find the thread to drop!");
361 return;
362 }
363 emp = &(*emp)->mNextThread;
364 }
365 *emp = thread->mNextThread;
366 thread->ReleaseQuiet();
367 thread = nsnull;
368}
369
370/*static*/ void nsExceptionService::DropThread(nsExceptionManager *thread)
371{
372 PR_Lock(lock);
373 DoDropThread(thread);
374 PR_Unlock(lock);
375}
376
377/*static*/ void nsExceptionService::DropAllThreads()
378{
379 PR_Lock(lock);
380 while (firstThread)
381 DoDropThread(firstThread);
382 PR_Unlock(lock);
383}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette