1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Netscape Public License
6
* Version 1.1 (the "License"); you may not use this file except in
7
* compliance with the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/NPL/
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
15
* The Original Code is mozilla.org code.
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.
23
* Mark Hammond <MarkH@ActiveState.com>
25
* Alternatively, the contents of this file may be used under the terms of
26
* either the GNU General Public License Version 2 or later (the "GPL"), or
27
* 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 NPL, 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 NPL, the GPL or the LGPL.
37
* ***** END LICENSE BLOCK ***** */
39
#include "nsISupports.h"
40
#include "nsExceptionService.h"
41
#include "nsIServiceManager.h"
45
static const PRUintn BAD_TLS_INDEX = (PRUintn) -1;
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
50
// A key for our registered module providers hashtable
51
class nsProviderKey : public nsHashKey {
55
nsProviderKey(PRUint32 key) : mKey(key) {}
56
PRUint32 HashCode(void) const {
59
PRBool Equals(const nsHashKey *aKey) const {
60
return mKey == ((const nsProviderKey *) aKey)->mKey;
62
nsHashKey *Clone() const {
63
return new nsProviderKey(mKey);
65
PRUint32 GetValue() { return mKey; }
68
/** Exception Manager definition **/
69
class nsExceptionManager : public nsIExceptionManager
73
NS_DECL_NSIEXCEPTIONMANAGER
75
nsExceptionManager(nsExceptionService *svc);
76
/* additional members */
77
nsCOMPtr<nsIException> mCurrentException;
78
nsExceptionManager *mNextThread; // not ref-counted.
79
nsExceptionService *mService; // not ref-counted
81
static PRInt32 totalInstances;
85
~nsExceptionManager();
90
PRInt32 nsExceptionManager::totalInstances = 0;
93
// Note this object is single threaded - the service itself ensures
95
// An exception if the destructor, which may be called on
96
// the thread shutting down xpcom
97
NS_IMPL_ISUPPORTS1(nsExceptionManager, nsIExceptionManager)
99
nsExceptionManager::nsExceptionManager(nsExceptionService *svc) :
103
/* member initializers and constructor code */
105
PR_AtomicIncrement(&totalInstances);
109
nsExceptionManager::~nsExceptionManager()
111
/* destructor code */
113
PR_AtomicDecrement(&totalInstances);
117
/* void setCurrentException (in nsIException error); */
118
NS_IMETHODIMP nsExceptionManager::SetCurrentException(nsIException *error)
120
CHECK_MANAGER_USE_OK();
121
mCurrentException = error;
125
/* nsIException getCurrentException (); */
126
NS_IMETHODIMP nsExceptionManager::GetCurrentException(nsIException **_retval)
128
CHECK_MANAGER_USE_OK();
129
*_retval = mCurrentException;
130
NS_IF_ADDREF(*_retval);
134
/* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
135
NS_IMETHODIMP nsExceptionManager::GetExceptionFromProvider(nsresult rc, nsIException * defaultException, nsIException **_retval)
137
CHECK_MANAGER_USE_OK();
138
// Just delegate back to the service with the provider map.
139
return mService->GetExceptionFromProvider(rc, defaultException, _retval);
142
/* The Exception Service */
144
PRUintn nsExceptionService::tlsIndex = BAD_TLS_INDEX;
145
PRLock *nsExceptionService::lock = PR_FALSE;
146
nsExceptionManager *nsExceptionService::firstThread = nsnull;
149
PRInt32 nsExceptionService::totalInstances = 0;
152
NS_IMPL_THREADSAFE_ISUPPORTS2(nsExceptionService, nsIExceptionService, nsIObserver)
154
nsExceptionService::nsExceptionService()
155
: mProviders(4, PR_TRUE) /* small, thread-safe hashtable */
158
if (PR_AtomicIncrement(&totalInstances)!=1) {
159
NS_ERROR("The nsExceptionService is a singleton!");
162
/* member initializers and constructor code */
163
if (tlsIndex == BAD_TLS_INDEX) {
165
status = PR_NewThreadPrivateIndex( &tlsIndex, ThreadDestruct );
166
NS_WARN_IF_FALSE(status==0, "ScriptErrorService could not allocate TLS storage.");
169
NS_WARN_IF_FALSE(lock, "Error allocating ExceptionService lock");
171
// observe XPCOM shutdown.
172
nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1");
173
NS_WARN_IF_FALSE(observerService, "Could not get observer service!");
175
observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
178
nsExceptionService::~nsExceptionService()
181
/* destructor code */
183
PR_AtomicDecrement(&totalInstances);
188
void nsExceptionService::ThreadDestruct( void *data )
191
NS_WARNING("nsExceptionService ignoring thread destruction after shutdown");
194
DropThread( (nsExceptionManager *)data );
198
void nsExceptionService::Shutdown()
203
PR_DestroyLock(lock);
206
PR_SetThreadPrivate(tlsIndex, nsnull);
209
/* void setCurrentException (in nsIException error); */
210
NS_IMETHODIMP nsExceptionService::SetCurrentException(nsIException *error)
212
CHECK_SERVICE_USE_OK();
213
nsCOMPtr<nsIExceptionManager> sm;
214
nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm));
217
return sm->SetCurrentException(error);
220
/* nsIException getCurrentException (); */
221
NS_IMETHODIMP nsExceptionService::GetCurrentException(nsIException **_retval)
223
CHECK_SERVICE_USE_OK();
224
nsCOMPtr<nsIExceptionManager> sm;
225
nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm));
228
return sm->GetCurrentException(_retval);
231
/* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
232
NS_IMETHODIMP nsExceptionService::GetExceptionFromProvider(nsresult rc,
233
nsIException * defaultException, nsIException **_retval)
235
CHECK_SERVICE_USE_OK();
236
return DoGetExceptionFromProvider(rc, defaultException, _retval);
239
/* readonly attribute nsIExceptionManager currentExceptionManager; */
240
NS_IMETHODIMP nsExceptionService::GetCurrentExceptionManager(nsIExceptionManager * *aCurrentScriptManager)
242
CHECK_SERVICE_USE_OK();
243
nsExceptionManager *mgr = (nsExceptionManager *)PR_GetThreadPrivate(tlsIndex);
245
// Stick the new exception object in with no reference count.
246
mgr = new nsExceptionManager(this);
248
return NS_ERROR_OUT_OF_MEMORY;
249
PR_SetThreadPrivate(tlsIndex, mgr);
250
// The reference count is held in the thread-list
253
*aCurrentScriptManager = mgr;
254
NS_ADDREF(*aCurrentScriptManager);
258
/* void registerErrorProvider (in nsIExceptionProvider provider, in PRUint32 moduleCode); */
259
NS_IMETHODIMP nsExceptionService::RegisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule)
261
CHECK_SERVICE_USE_OK();
263
nsProviderKey key(errorModule);
264
if (mProviders.Put(&key, provider)) {
265
NS_WARNING("Registration of exception provider overwrote another provider with the same module code!");
270
/* void unregisterErrorProvider (in nsIExceptionProvider provider, in PRUint32 errorModule); */
271
NS_IMETHODIMP nsExceptionService::UnregisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule)
273
CHECK_SERVICE_USE_OK();
274
nsProviderKey key(errorModule);
275
if (!mProviders.Remove(&key)) {
276
NS_WARNING("Attempt to unregister an unregistered exception provider!");
277
return NS_ERROR_UNEXPECTED;
283
NS_IMETHODIMP nsExceptionService::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
290
nsExceptionService::DoGetExceptionFromProvider(nsresult errCode,
291
nsIException * defaultException,
294
// Check for an existing exception
295
nsresult nr = GetCurrentException(_exc);
296
if (NS_SUCCEEDED(nr) && *_exc) {
297
(*_exc)->GetResult(&nr);
298
// If it matches our result then use it
303
nsProviderKey key(NS_ERROR_GET_MODULE(errCode));
304
nsCOMPtr<nsIExceptionProvider> provider =
305
dont_AddRef((nsIExceptionProvider *)mProviders.Get(&key));
307
// No provider so we'll return the default exception
309
*_exc = defaultException;
314
return provider->GetException(errCode, defaultException, _exc);
318
/*static*/ void nsExceptionService::AddThread(nsExceptionManager *thread)
321
thread->mNextThread = firstThread;
322
firstThread = thread;
327
/*static*/ void nsExceptionService::DoDropThread(nsExceptionManager *thread)
329
nsExceptionManager **emp = &firstThread;
330
while (*emp != thread) {
331
NS_ABORT_IF_FALSE(*emp, "Could not find the thread to drop!");
332
emp = &(*emp)->mNextThread;
334
*emp = thread->mNextThread;
338
/*static*/ void nsExceptionService::DropThread(nsExceptionManager *thread)
341
DoDropThread(thread);
345
/*static*/ void nsExceptionService::DropAllThreads()
349
DoDropThread(firstThread);