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
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/
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 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.
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
= nsnull
;
146 nsExceptionManager
*nsExceptionService::firstThread
= nsnull
;
149 PRInt32
nsExceptionService::totalInstances
= 0;
152 NS_IMPL_THREADSAFE_ISUPPORTS3(nsExceptionService
,
157 nsExceptionService::nsExceptionService()
158 : mProviders(4, PR_TRUE
) /* small, thread-safe hashtable */
161 if (PR_AtomicIncrement(&totalInstances
)!=1) {
162 NS_ERROR("The nsExceptionService is a singleton!");
165 /* member initializers and constructor code */
166 if (tlsIndex
== BAD_TLS_INDEX
) {
168 status
= PR_NewThreadPrivateIndex( &tlsIndex
, ThreadDestruct
);
169 NS_ASSERTION(status
==0, "ScriptErrorService could not allocate TLS storage.");
172 NS_ASSERTION(lock
, "Error allocating ExceptionService lock");
174 // observe XPCOM shutdown.
175 nsCOMPtr
<nsIObserverService
> observerService
= do_GetService("@mozilla.org/observer-service;1");
176 NS_ASSERTION(observerService
, "Could not get observer service!");
178 observerService
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, PR_FALSE
);
181 nsExceptionService::~nsExceptionService()
184 /* destructor code */
186 PR_AtomicDecrement(&totalInstances
);
191 void nsExceptionService::ThreadDestruct( void *data
)
194 NS_WARNING("nsExceptionService ignoring thread destruction after shutdown");
197 DropThread( (nsExceptionManager
*)data
);
201 void nsExceptionService::Shutdown()
206 PR_DestroyLock(lock
);
209 PR_SetThreadPrivate(tlsIndex
, nsnull
);
212 /* void setCurrentException (in nsIException error); */
213 NS_IMETHODIMP
nsExceptionService::SetCurrentException(nsIException
*error
)
215 CHECK_SERVICE_USE_OK();
216 nsCOMPtr
<nsIExceptionManager
> sm
;
217 nsresult nr
= GetCurrentExceptionManager(getter_AddRefs(sm
));
220 return sm
->SetCurrentException(error
);
223 /* nsIException getCurrentException (); */
224 NS_IMETHODIMP
nsExceptionService::GetCurrentException(nsIException
**_retval
)
226 CHECK_SERVICE_USE_OK();
227 nsCOMPtr
<nsIExceptionManager
> sm
;
228 nsresult nr
= GetCurrentExceptionManager(getter_AddRefs(sm
));
231 return sm
->GetCurrentException(_retval
);
234 /* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
235 NS_IMETHODIMP
nsExceptionService::GetExceptionFromProvider(nsresult rc
,
236 nsIException
* defaultException
, nsIException
**_retval
)
238 CHECK_SERVICE_USE_OK();
239 return DoGetExceptionFromProvider(rc
, defaultException
, _retval
);
242 /* readonly attribute nsIExceptionManager currentExceptionManager; */
243 NS_IMETHODIMP
nsExceptionService::GetCurrentExceptionManager(nsIExceptionManager
* *aCurrentScriptManager
)
245 CHECK_SERVICE_USE_OK();
246 nsExceptionManager
*mgr
= (nsExceptionManager
*)PR_GetThreadPrivate(tlsIndex
);
248 // Stick the new exception object in with no reference count.
249 mgr
= new nsExceptionManager(this);
251 return NS_ERROR_OUT_OF_MEMORY
;
252 PR_SetThreadPrivate(tlsIndex
, mgr
);
253 // The reference count is held in the thread-list
256 *aCurrentScriptManager
= mgr
;
257 NS_ADDREF(*aCurrentScriptManager
);
261 /* void registerErrorProvider (in nsIExceptionProvider provider, in PRUint32 moduleCode); */
262 NS_IMETHODIMP
nsExceptionService::RegisterExceptionProvider(nsIExceptionProvider
*provider
, PRUint32 errorModule
)
264 CHECK_SERVICE_USE_OK();
266 nsProviderKey
key(errorModule
);
267 if (mProviders
.Put(&key
, provider
)) {
268 NS_WARNING("Registration of exception provider overwrote another provider with the same module code!");
273 /* void unregisterErrorProvider (in nsIExceptionProvider provider, in PRUint32 errorModule); */
274 NS_IMETHODIMP
nsExceptionService::UnregisterExceptionProvider(nsIExceptionProvider
*provider
, PRUint32 errorModule
)
276 CHECK_SERVICE_USE_OK();
277 nsProviderKey
key(errorModule
);
278 if (!mProviders
.Remove(&key
)) {
279 NS_WARNING("Attempt to unregister an unregistered exception provider!");
280 return NS_ERROR_UNEXPECTED
;
286 NS_IMETHODIMP
nsExceptionService::Observe(nsISupports
*aSubject
, const char *aTopic
, const PRUnichar
*someData
)
293 nsExceptionService::DoGetExceptionFromProvider(nsresult errCode
,
294 nsIException
* defaultException
,
297 // Check for an existing exception
298 nsresult nr
= GetCurrentException(_exc
);
299 if (NS_SUCCEEDED(nr
) && *_exc
) {
300 (*_exc
)->GetResult(&nr
);
301 // If it matches our result then use it
306 nsProviderKey
key(NS_ERROR_GET_MODULE(errCode
));
307 nsCOMPtr
<nsIExceptionProvider
> provider
=
308 dont_AddRef((nsIExceptionProvider
*)mProviders
.Get(&key
));
310 // No provider so we'll return the default exception
312 *_exc
= defaultException
;
317 return provider
->GetException(errCode
, defaultException
, _exc
);
321 /*static*/ void nsExceptionService::AddThread(nsExceptionManager
*thread
)
324 thread
->mNextThread
= firstThread
;
325 firstThread
= thread
;
330 /*static*/ void nsExceptionService::DoDropThread(nsExceptionManager
*thread
)
332 nsExceptionManager
**emp
= &firstThread
;
333 while (*emp
!= thread
) {
334 NS_ABORT_IF_FALSE(*emp
, "Could not find the thread to drop!");
335 emp
= &(*emp
)->mNextThread
;
337 *emp
= thread
->mNextThread
;
341 /*static*/ void nsExceptionService::DropThread(nsExceptionManager
*thread
)
344 DoDropThread(thread
);
348 /*static*/ void nsExceptionService::DropAllThreads()
352 DoDropThread(firstThread
);