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 #include "mozilla/Services.h"
47 static const PRUintn BAD_TLS_INDEX
= (PRUintn
) -1;
49 #define CHECK_SERVICE_USE_OK() if (!lock) return NS_ERROR_NOT_INITIALIZED
50 #define CHECK_MANAGER_USE_OK() if (!mService || !nsExceptionService::lock) return NS_ERROR_NOT_INITIALIZED
52 // A key for our registered module providers hashtable
53 class nsProviderKey
: public nsHashKey
{
57 nsProviderKey(PRUint32 key
) : mKey(key
) {}
58 PRUint32
HashCode(void) const {
61 PRBool
Equals(const nsHashKey
*aKey
) const {
62 return mKey
== ((const nsProviderKey
*) aKey
)->mKey
;
64 nsHashKey
*Clone() const {
65 return new nsProviderKey(mKey
);
67 PRUint32
GetValue() { return mKey
; }
70 /** Exception Manager definition **/
71 class nsExceptionManager
: public nsIExceptionManager
75 NS_DECL_NSIEXCEPTIONMANAGER
77 nsExceptionManager(nsExceptionService
*svc
);
78 /* additional members */
79 nsCOMPtr
<nsIException
> mCurrentException
;
80 nsExceptionManager
*mNextThread
; // not ref-counted.
81 nsExceptionService
*mService
; // not ref-counted
83 static PRInt32 totalInstances
;
87 ~nsExceptionManager();
92 PRInt32
nsExceptionManager::totalInstances
= 0;
95 // Note this object is single threaded - the service itself ensures
97 // An exception if the destructor, which may be called on
98 // the thread shutting down xpcom
99 NS_IMPL_ISUPPORTS1(nsExceptionManager
, nsIExceptionManager
)
101 nsExceptionManager::nsExceptionManager(nsExceptionService
*svc
) :
105 /* member initializers and constructor code */
107 PR_AtomicIncrement(&totalInstances
);
111 nsExceptionManager::~nsExceptionManager()
113 /* destructor code */
115 PR_AtomicDecrement(&totalInstances
);
119 /* void setCurrentException (in nsIException error); */
120 NS_IMETHODIMP
nsExceptionManager::SetCurrentException(nsIException
*error
)
122 CHECK_MANAGER_USE_OK();
123 mCurrentException
= error
;
127 /* nsIException getCurrentException (); */
128 NS_IMETHODIMP
nsExceptionManager::GetCurrentException(nsIException
**_retval
)
130 CHECK_MANAGER_USE_OK();
131 *_retval
= mCurrentException
;
132 NS_IF_ADDREF(*_retval
);
136 /* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
137 NS_IMETHODIMP
nsExceptionManager::GetExceptionFromProvider(nsresult rc
, nsIException
* defaultException
, nsIException
**_retval
)
139 CHECK_MANAGER_USE_OK();
140 // Just delegate back to the service with the provider map.
141 return mService
->GetExceptionFromProvider(rc
, defaultException
, _retval
);
144 /* The Exception Service */
146 PRUintn
nsExceptionService::tlsIndex
= BAD_TLS_INDEX
;
147 PRLock
*nsExceptionService::lock
= nsnull
;
148 nsExceptionManager
*nsExceptionService::firstThread
= nsnull
;
151 PRInt32
nsExceptionService::totalInstances
= 0;
154 NS_IMPL_THREADSAFE_ISUPPORTS3(nsExceptionService
,
159 nsExceptionService::nsExceptionService()
160 : mProviders(4, PR_TRUE
) /* small, thread-safe hashtable */
163 if (PR_AtomicIncrement(&totalInstances
)!=1) {
164 NS_ERROR("The nsExceptionService is a singleton!");
167 /* member initializers and constructor code */
168 if (tlsIndex
== BAD_TLS_INDEX
) {
170 status
= PR_NewThreadPrivateIndex( &tlsIndex
, ThreadDestruct
);
171 NS_ASSERTION(status
==0, "ScriptErrorService could not allocate TLS storage.");
174 NS_ASSERTION(lock
, "Error allocating ExceptionService lock");
176 // observe XPCOM shutdown.
177 nsCOMPtr
<nsIObserverService
> observerService
=
178 mozilla::services::GetObserverService();
179 NS_ASSERTION(observerService
, "Could not get observer service!");
181 observerService
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, PR_FALSE
);
184 nsExceptionService::~nsExceptionService()
187 /* destructor code */
189 PR_AtomicDecrement(&totalInstances
);
194 void nsExceptionService::ThreadDestruct( void *data
)
197 NS_WARNING("nsExceptionService ignoring thread destruction after shutdown");
200 DropThread( (nsExceptionManager
*)data
);
204 void nsExceptionService::Shutdown()
209 PR_DestroyLock(lock
);
212 PR_SetThreadPrivate(tlsIndex
, nsnull
);
215 /* void setCurrentException (in nsIException error); */
216 NS_IMETHODIMP
nsExceptionService::SetCurrentException(nsIException
*error
)
218 CHECK_SERVICE_USE_OK();
219 nsCOMPtr
<nsIExceptionManager
> sm
;
220 nsresult nr
= GetCurrentExceptionManager(getter_AddRefs(sm
));
223 return sm
->SetCurrentException(error
);
226 /* nsIException getCurrentException (); */
227 NS_IMETHODIMP
nsExceptionService::GetCurrentException(nsIException
**_retval
)
229 CHECK_SERVICE_USE_OK();
230 nsCOMPtr
<nsIExceptionManager
> sm
;
231 nsresult nr
= GetCurrentExceptionManager(getter_AddRefs(sm
));
234 return sm
->GetCurrentException(_retval
);
237 /* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
238 NS_IMETHODIMP
nsExceptionService::GetExceptionFromProvider(nsresult rc
,
239 nsIException
* defaultException
, nsIException
**_retval
)
241 CHECK_SERVICE_USE_OK();
242 return DoGetExceptionFromProvider(rc
, defaultException
, _retval
);
245 /* readonly attribute nsIExceptionManager currentExceptionManager; */
246 NS_IMETHODIMP
nsExceptionService::GetCurrentExceptionManager(nsIExceptionManager
* *aCurrentScriptManager
)
248 CHECK_SERVICE_USE_OK();
249 nsExceptionManager
*mgr
= (nsExceptionManager
*)PR_GetThreadPrivate(tlsIndex
);
251 // Stick the new exception object in with no reference count.
252 mgr
= new nsExceptionManager(this);
254 return NS_ERROR_OUT_OF_MEMORY
;
255 PR_SetThreadPrivate(tlsIndex
, mgr
);
256 // The reference count is held in the thread-list
259 *aCurrentScriptManager
= mgr
;
260 NS_ADDREF(*aCurrentScriptManager
);
264 /* void registerErrorProvider (in nsIExceptionProvider provider, in PRUint32 moduleCode); */
265 NS_IMETHODIMP
nsExceptionService::RegisterExceptionProvider(nsIExceptionProvider
*provider
, PRUint32 errorModule
)
267 CHECK_SERVICE_USE_OK();
269 nsProviderKey
key(errorModule
);
270 if (mProviders
.Put(&key
, provider
)) {
271 NS_WARNING("Registration of exception provider overwrote another provider with the same module code!");
276 /* void unregisterErrorProvider (in nsIExceptionProvider provider, in PRUint32 errorModule); */
277 NS_IMETHODIMP
nsExceptionService::UnregisterExceptionProvider(nsIExceptionProvider
*provider
, PRUint32 errorModule
)
279 CHECK_SERVICE_USE_OK();
280 nsProviderKey
key(errorModule
);
281 if (!mProviders
.Remove(&key
)) {
282 NS_WARNING("Attempt to unregister an unregistered exception provider!");
283 return NS_ERROR_UNEXPECTED
;
289 NS_IMETHODIMP
nsExceptionService::Observe(nsISupports
*aSubject
, const char *aTopic
, const PRUnichar
*someData
)
296 nsExceptionService::DoGetExceptionFromProvider(nsresult errCode
,
297 nsIException
* defaultException
,
300 // Check for an existing exception
301 nsresult nr
= GetCurrentException(_exc
);
302 if (NS_SUCCEEDED(nr
) && *_exc
) {
303 (*_exc
)->GetResult(&nr
);
304 // If it matches our result then use it
309 nsProviderKey
key(NS_ERROR_GET_MODULE(errCode
));
310 nsCOMPtr
<nsIExceptionProvider
> provider
=
311 dont_AddRef((nsIExceptionProvider
*)mProviders
.Get(&key
));
313 // No provider so we'll return the default exception
315 *_exc
= defaultException
;
320 return provider
->GetException(errCode
, defaultException
, _exc
);
324 /*static*/ void nsExceptionService::AddThread(nsExceptionManager
*thread
)
327 thread
->mNextThread
= firstThread
;
328 firstThread
= thread
;
333 /*static*/ void nsExceptionService::DoDropThread(nsExceptionManager
*thread
)
335 nsExceptionManager
**emp
= &firstThread
;
336 while (*emp
!= thread
) {
337 NS_ABORT_IF_FALSE(*emp
, "Could not find the thread to drop!");
338 emp
= &(*emp
)->mNextThread
;
340 *emp
= thread
->mNextThread
;
344 /*static*/ void nsExceptionService::DropThread(nsExceptionManager
*thread
)
347 DoDropThread(thread
);
351 /*static*/ void nsExceptionService::DropAllThreads()
355 DoDropThread(firstThread
);