VirtualBox

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

Last change on this file since 549 was 1, checked in by vboxsync, 55 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.7 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 nsAutoOwningThread old = _mOwningThread;
87 _mOwningThread = nsAutoOwningThread();
88 nsrefcnt ref = Release();
89 _mOwningThread = old;
90 return ref;
91 }
92#else
93 inline nsrefcnt ReleaseQuiet(void) { return Release(); }
94#endif
95
96private:
97 ~nsExceptionManager();
98};
99
100
101#ifdef NS_DEBUG
102PRInt32 nsExceptionManager::totalInstances = 0;
103#endif
104
105// Note this object is single threaded - the service itself ensures
106// one per thread.
107// Exceptions are DropAllThreads (called on the thread shutting down xpcom)
108// and ThreadDestruct (called after xpcom destroyed the internal thread struct,
109// so that PR_GetCurrentThread() will create a new one from scratch which will
110// obviously not match the old one stored in the instance on creation). Since
111// these NS_CheckThreadSafe() assertions are quite annoying in the debug build,
112// we use a special ReleaseQuiet() mehtod in DoDropThread() to shut them up.
113NS_IMPL_ISUPPORTS1(nsExceptionManager, nsIExceptionManager)
114
115nsExceptionManager::nsExceptionManager(nsExceptionService *svc) :
116 mNextThread(nsnull),
117 mService(svc)
118{
119 /* member initializers and constructor code */
120#ifdef NS_DEBUG
121 PR_AtomicIncrement(&totalInstances);
122#endif
123}
124
125nsExceptionManager::~nsExceptionManager()
126{
127 /* destructor code */
128#ifdef NS_DEBUG
129 PR_AtomicDecrement(&totalInstances);
130#endif // NS_DEBUG
131}
132
133/* void setCurrentException (in nsIException error); */
134NS_IMETHODIMP nsExceptionManager::SetCurrentException(nsIException *error)
135{
136 CHECK_MANAGER_USE_OK();
137 mCurrentException = error;
138 return NS_OK;
139}
140
141/* nsIException getCurrentException (); */
142NS_IMETHODIMP nsExceptionManager::GetCurrentException(nsIException **_retval)
143{
144 CHECK_MANAGER_USE_OK();
145 *_retval = mCurrentException;
146 NS_IF_ADDREF(*_retval);
147 return NS_OK;
148}
149
150/* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
151NS_IMETHODIMP nsExceptionManager::GetExceptionFromProvider(nsresult rc, nsIException * defaultException, nsIException **_retval)
152{
153 CHECK_MANAGER_USE_OK();
154 // Just delegate back to the service with the provider map.
155 return mService->GetExceptionFromProvider(rc, defaultException, _retval);
156}
157
158/* The Exception Service */
159
160PRUintn nsExceptionService::tlsIndex = BAD_TLS_INDEX;
161PRLock *nsExceptionService::lock = PR_FALSE;
162nsExceptionManager *nsExceptionService::firstThread = nsnull;
163
164#ifdef NS_DEBUG
165PRInt32 nsExceptionService::totalInstances = 0;
166#endif
167
168NS_IMPL_THREADSAFE_ISUPPORTS2(nsExceptionService, nsIExceptionService, nsIObserver)
169
170nsExceptionService::nsExceptionService()
171 : mProviders(4, PR_TRUE) /* small, thread-safe hashtable */
172{
173#ifdef NS_DEBUG
174 if (PR_AtomicIncrement(&totalInstances)!=1) {
175 NS_ERROR("The nsExceptionService is a singleton!");
176 }
177#endif
178 /* member initializers and constructor code */
179 if (tlsIndex == BAD_TLS_INDEX) {
180 PRStatus status;
181 status = PR_NewThreadPrivateIndex( &tlsIndex, ThreadDestruct );
182 NS_WARN_IF_FALSE(status==0, "ScriptErrorService could not allocate TLS storage.");
183 }
184 lock = PR_NewLock();
185 NS_WARN_IF_FALSE(lock, "Error allocating ExceptionService lock");
186
187 // observe XPCOM shutdown.
188 nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1");
189 NS_WARN_IF_FALSE(observerService, "Could not get observer service!");
190 if (observerService)
191 observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
192}
193
194nsExceptionService::~nsExceptionService()
195{
196 Shutdown();
197 /* destructor code */
198#ifdef NS_DEBUG
199 PR_AtomicDecrement(&totalInstances);
200#endif
201}
202
203/*static*/
204void nsExceptionService::ThreadDestruct( void *data )
205{
206 if (!lock) {
207 // a typical situation is when IPC worker threads that have instantiated
208 // exception managers are stopped after nsExceptionService is shut down,
209 // which will result into this warning. disable it.
210#if 0
211 NS_WARNING("nsExceptionService ignoring thread destruction after shutdown");
212#endif
213 return;
214 }
215 DropThread( (nsExceptionManager *)data );
216}
217
218
219void nsExceptionService::Shutdown()
220{
221 mProviders.Reset();
222 if (lock) {
223 DropAllThreads();
224 PR_DestroyLock(lock);
225 lock = nsnull;
226 }
227 PR_SetThreadPrivate(tlsIndex, nsnull);
228}
229
230/* void setCurrentException (in nsIException error); */
231NS_IMETHODIMP nsExceptionService::SetCurrentException(nsIException *error)
232{
233 CHECK_SERVICE_USE_OK();
234 nsCOMPtr<nsIExceptionManager> sm;
235 nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm));
236 if (NS_FAILED(nr))
237 return nr;
238 return sm->SetCurrentException(error);
239}
240
241/* nsIException getCurrentException (); */
242NS_IMETHODIMP nsExceptionService::GetCurrentException(nsIException **_retval)
243{
244 CHECK_SERVICE_USE_OK();
245 nsCOMPtr<nsIExceptionManager> sm;
246 nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm));
247 if (NS_FAILED(nr))
248 return nr;
249 return sm->GetCurrentException(_retval);
250}
251
252/* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
253NS_IMETHODIMP nsExceptionService::GetExceptionFromProvider(nsresult rc,
254 nsIException * defaultException, nsIException **_retval)
255{
256 CHECK_SERVICE_USE_OK();
257 return DoGetExceptionFromProvider(rc, defaultException, _retval);
258}
259
260/* readonly attribute nsIExceptionManager currentExceptionManager; */
261NS_IMETHODIMP nsExceptionService::GetCurrentExceptionManager(nsIExceptionManager * *aCurrentScriptManager)
262{
263 CHECK_SERVICE_USE_OK();
264 nsExceptionManager *mgr = (nsExceptionManager *)PR_GetThreadPrivate(tlsIndex);
265 if (mgr == nsnull) {
266 // Stick the new exception object in with no reference count.
267 mgr = new nsExceptionManager(this);
268 if (mgr == nsnull)
269 return NS_ERROR_OUT_OF_MEMORY;
270 PR_SetThreadPrivate(tlsIndex, mgr);
271 // The reference count is held in the thread-list
272 AddThread(mgr);
273 }
274 *aCurrentScriptManager = mgr;
275 NS_ADDREF(*aCurrentScriptManager);
276 return NS_OK;
277}
278
279/* void registerErrorProvider (in nsIExceptionProvider provider, in PRUint32 moduleCode); */
280NS_IMETHODIMP nsExceptionService::RegisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule)
281{
282 CHECK_SERVICE_USE_OK();
283
284 nsProviderKey key(errorModule);
285 if (mProviders.Put(&key, provider)) {
286 NS_WARNING("Registration of exception provider overwrote another provider with the same module code!");
287 }
288 return NS_OK;
289}
290
291/* void unregisterErrorProvider (in nsIExceptionProvider provider, in PRUint32 errorModule); */
292NS_IMETHODIMP nsExceptionService::UnregisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule)
293{
294 CHECK_SERVICE_USE_OK();
295 nsProviderKey key(errorModule);
296 if (!mProviders.Remove(&key)) {
297 NS_WARNING("Attempt to unregister an unregistered exception provider!");
298 return NS_ERROR_UNEXPECTED;
299 }
300 return NS_OK;
301}
302
303// nsIObserver
304NS_IMETHODIMP nsExceptionService::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
305{
306 Shutdown();
307 return NS_OK;
308}
309
310nsresult
311nsExceptionService::DoGetExceptionFromProvider(nsresult errCode,
312 nsIException * defaultException,
313 nsIException **_exc)
314{
315 // Check for an existing exception
316 nsresult nr = GetCurrentException(_exc);
317 if (NS_SUCCEEDED(nr) && *_exc) {
318 (*_exc)->GetResult(&nr);
319 // If it matches our result then use it
320 if (nr == errCode)
321 return NS_OK;
322 NS_RELEASE(*_exc);
323 }
324 nsProviderKey key(NS_ERROR_GET_MODULE(errCode));
325 nsCOMPtr<nsIExceptionProvider> provider =
326 dont_AddRef((nsIExceptionProvider *)mProviders.Get(&key));
327
328 // No provider so we'll return the default exception
329 if (!provider) {
330 *_exc = defaultException;
331 NS_IF_ADDREF(*_exc);
332 return NS_OK;
333 }
334
335 return provider->GetException(errCode, defaultException, _exc);
336}
337
338// thread management
339/*static*/ void nsExceptionService::AddThread(nsExceptionManager *thread)
340{
341 PR_Lock(lock);
342 thread->mNextThread = firstThread;
343 firstThread = thread;
344 NS_ADDREF(thread);
345 PR_Unlock(lock);
346}
347
348/*static*/ void nsExceptionService::DoDropThread(nsExceptionManager *thread)
349{
350 nsExceptionManager **emp = &firstThread;
351 while (*emp != thread) {
352 NS_ABORT_IF_FALSE(*emp, "Could not find the thread to drop!");
353 emp = &(*emp)->mNextThread;
354 }
355 *emp = thread->mNextThread;
356 thread->ReleaseQuiet();
357 thread = 0;
358}
359
360/*static*/ void nsExceptionService::DropThread(nsExceptionManager *thread)
361{
362 PR_Lock(lock);
363 DoDropThread(thread);
364 PR_Unlock(lock);
365}
366
367/*static*/ void nsExceptionService::DropAllThreads()
368{
369 PR_Lock(lock);
370 while (firstThread)
371 DoDropThread(firstThread);
372 PR_Unlock(lock);
373}
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