VirtualBox

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

Last change on this file since 62330 was 62170, checked in by vboxsync, 8 years ago

XPCOM: Change the shutdown order in nsExceptionService, attempting to fix a sporadic crash. The biggest change is postponing the lock freeing to the destructor. The order change of the operations in the Shutdown method eliminates the concerns mention in the ThreadDestruct method. The change in DoDropThread is pure paranoia, intended to avoid potential crashes due to continuing with a NULL pointer in *emp if the thread is not known.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.0 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 (!lock) return NS_ERROR_NOT_INITIALIZED
48#define CHECK_MANAGER_USE_OK() if (!mService || !nsExceptionService::lock) 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 PR_SetThreadPrivate(tlsIndex, nsnull);
228 mProviders.Reset();
229 if (lock) {
230 DropAllThreads();
231 }
232}
233
234/* void setCurrentException (in nsIException error); */
235NS_IMETHODIMP nsExceptionService::SetCurrentException(nsIException *error)
236{
237 CHECK_SERVICE_USE_OK();
238 nsCOMPtr<nsIExceptionManager> sm;
239 nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm));
240 if (NS_FAILED(nr))
241 return nr;
242 return sm->SetCurrentException(error);
243}
244
245/* nsIException getCurrentException (); */
246NS_IMETHODIMP nsExceptionService::GetCurrentException(nsIException **_retval)
247{
248 CHECK_SERVICE_USE_OK();
249 nsCOMPtr<nsIExceptionManager> sm;
250 nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm));
251 if (NS_FAILED(nr))
252 return nr;
253 return sm->GetCurrentException(_retval);
254}
255
256/* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
257NS_IMETHODIMP nsExceptionService::GetExceptionFromProvider(nsresult rc,
258 nsIException * defaultException, nsIException **_retval)
259{
260 CHECK_SERVICE_USE_OK();
261 return DoGetExceptionFromProvider(rc, defaultException, _retval);
262}
263
264/* readonly attribute nsIExceptionManager currentExceptionManager; */
265NS_IMETHODIMP nsExceptionService::GetCurrentExceptionManager(nsIExceptionManager * *aCurrentScriptManager)
266{
267 CHECK_SERVICE_USE_OK();
268 nsExceptionManager *mgr = (nsExceptionManager *)PR_GetThreadPrivate(tlsIndex);
269 if (mgr == nsnull) {
270 // Stick the new exception object in with no reference count.
271 mgr = new nsExceptionManager(this);
272 if (mgr == nsnull)
273 return NS_ERROR_OUT_OF_MEMORY;
274 PR_SetThreadPrivate(tlsIndex, mgr);
275 // The reference count is held in the thread-list
276 AddThread(mgr);
277 }
278 *aCurrentScriptManager = mgr;
279 NS_ADDREF(*aCurrentScriptManager);
280 return NS_OK;
281}
282
283/* void registerErrorProvider (in nsIExceptionProvider provider, in PRUint32 moduleCode); */
284NS_IMETHODIMP nsExceptionService::RegisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule)
285{
286 CHECK_SERVICE_USE_OK();
287
288 nsProviderKey key(errorModule);
289 if (mProviders.Put(&key, provider)) {
290 NS_WARNING("Registration of exception provider overwrote another provider with the same module code!");
291 }
292 return NS_OK;
293}
294
295/* void unregisterErrorProvider (in nsIExceptionProvider provider, in PRUint32 errorModule); */
296NS_IMETHODIMP nsExceptionService::UnregisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule)
297{
298 CHECK_SERVICE_USE_OK();
299 nsProviderKey key(errorModule);
300 if (!mProviders.Remove(&key)) {
301 NS_WARNING("Attempt to unregister an unregistered exception provider!");
302 return NS_ERROR_UNEXPECTED;
303 }
304 return NS_OK;
305}
306
307// nsIObserver
308NS_IMETHODIMP nsExceptionService::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
309{
310 Shutdown();
311 return NS_OK;
312}
313
314nsresult
315nsExceptionService::DoGetExceptionFromProvider(nsresult errCode,
316 nsIException * defaultException,
317 nsIException **_exc)
318{
319 // Check for an existing exception
320 nsresult nr = GetCurrentException(_exc);
321 if (NS_SUCCEEDED(nr) && *_exc) {
322 (*_exc)->GetResult(&nr);
323 // If it matches our result then use it
324 if (nr == errCode)
325 return NS_OK;
326 NS_RELEASE(*_exc);
327 }
328 nsProviderKey key(NS_ERROR_GET_MODULE(errCode));
329 nsCOMPtr<nsIExceptionProvider> provider =
330 dont_AddRef((nsIExceptionProvider *)mProviders.Get(&key));
331
332 // No provider so we'll return the default exception
333 if (!provider) {
334 *_exc = defaultException;
335 NS_IF_ADDREF(*_exc);
336 return NS_OK;
337 }
338
339 return provider->GetException(errCode, defaultException, _exc);
340}
341
342// thread management
343/*static*/ void nsExceptionService::AddThread(nsExceptionManager *thread)
344{
345 PR_Lock(lock);
346 thread->mNextThread = firstThread;
347 firstThread = thread;
348 NS_ADDREF(thread);
349 PR_Unlock(lock);
350}
351
352/*static*/ void nsExceptionService::DoDropThread(nsExceptionManager *thread)
353{
354 nsExceptionManager **emp = &firstThread;
355 while (*emp != thread) {
356 if (!*emp)
357 {
358 NS_WARNING("Could not find the thread to drop!");
359 return;
360 }
361 emp = &(*emp)->mNextThread;
362 }
363 *emp = thread->mNextThread;
364 thread->ReleaseQuiet();
365 thread = nsnull;
366}
367
368/*static*/ void nsExceptionService::DropThread(nsExceptionManager *thread)
369{
370 PR_Lock(lock);
371 DoDropThread(thread);
372 PR_Unlock(lock);
373}
374
375/*static*/ void nsExceptionService::DropAllThreads()
376{
377 PR_Lock(lock);
378 while (firstThread)
379 DoDropThread(firstThread);
380 PR_Unlock(lock);
381}
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