On x86 compilers without fastcall, simulate it when invoking traces and un-simulate...
[wine-gecko.git] / xpcom / base / nsExceptionService.cpp
blob3cd37d985a4e01e49d4ab96adb6810da3a5171b9
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
13 * License.
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.
22 * Contributor(s):
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"
42 #include "nsCOMPtr.h"
43 #include "prthread.h"
44 #include "prlock.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 {
52 protected:
53 PRUint32 mKey;
54 public:
55 nsProviderKey(PRUint32 key) : mKey(key) {}
56 PRUint32 HashCode(void) const {
57 return mKey;
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
71 public:
72 NS_DECL_ISUPPORTS
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
80 #ifdef NS_DEBUG
81 static PRInt32 totalInstances;
82 #endif
84 private:
85 ~nsExceptionManager();
89 #ifdef NS_DEBUG
90 PRInt32 nsExceptionManager::totalInstances = 0;
91 #endif
93 // Note this object is single threaded - the service itself ensures
94 // one per thread.
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) :
100 mNextThread(nsnull),
101 mService(svc)
103 /* member initializers and constructor code */
104 #ifdef NS_DEBUG
105 PR_AtomicIncrement(&totalInstances);
106 #endif
109 nsExceptionManager::~nsExceptionManager()
111 /* destructor code */
112 #ifdef NS_DEBUG
113 PR_AtomicDecrement(&totalInstances);
114 #endif // NS_DEBUG
117 /* void setCurrentException (in nsIException error); */
118 NS_IMETHODIMP nsExceptionManager::SetCurrentException(nsIException *error)
120 CHECK_MANAGER_USE_OK();
121 mCurrentException = error;
122 return NS_OK;
125 /* nsIException getCurrentException (); */
126 NS_IMETHODIMP nsExceptionManager::GetCurrentException(nsIException **_retval)
128 CHECK_MANAGER_USE_OK();
129 *_retval = mCurrentException;
130 NS_IF_ADDREF(*_retval);
131 return NS_OK;
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;
148 #ifdef NS_DEBUG
149 PRInt32 nsExceptionService::totalInstances = 0;
150 #endif
152 NS_IMPL_THREADSAFE_ISUPPORTS3(nsExceptionService,
153 nsIExceptionService,
154 nsIExceptionManager,
155 nsIObserver)
157 nsExceptionService::nsExceptionService()
158 : mProviders(4, PR_TRUE) /* small, thread-safe hashtable */
160 #ifdef NS_DEBUG
161 if (PR_AtomicIncrement(&totalInstances)!=1) {
162 NS_ERROR("The nsExceptionService is a singleton!");
164 #endif
165 /* member initializers and constructor code */
166 if (tlsIndex == BAD_TLS_INDEX) {
167 PRStatus status;
168 status = PR_NewThreadPrivateIndex( &tlsIndex, ThreadDestruct );
169 NS_ASSERTION(status==0, "ScriptErrorService could not allocate TLS storage.");
171 lock = PR_NewLock();
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!");
177 if (observerService)
178 observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
181 nsExceptionService::~nsExceptionService()
183 Shutdown();
184 /* destructor code */
185 #ifdef NS_DEBUG
186 PR_AtomicDecrement(&totalInstances);
187 #endif
190 /*static*/
191 void nsExceptionService::ThreadDestruct( void *data )
193 if (!lock) {
194 NS_WARNING("nsExceptionService ignoring thread destruction after shutdown");
195 return;
197 DropThread( (nsExceptionManager *)data );
201 void nsExceptionService::Shutdown()
203 mProviders.Reset();
204 if (lock) {
205 DropAllThreads();
206 PR_DestroyLock(lock);
207 lock = nsnull;
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));
218 if (NS_FAILED(nr))
219 return nr;
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));
229 if (NS_FAILED(nr))
230 return nr;
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);
247 if (mgr == nsnull) {
248 // Stick the new exception object in with no reference count.
249 mgr = new nsExceptionManager(this);
250 if (mgr == nsnull)
251 return NS_ERROR_OUT_OF_MEMORY;
252 PR_SetThreadPrivate(tlsIndex, mgr);
253 // The reference count is held in the thread-list
254 AddThread(mgr);
256 *aCurrentScriptManager = mgr;
257 NS_ADDREF(*aCurrentScriptManager);
258 return NS_OK;
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!");
270 return NS_OK;
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;
282 return NS_OK;
285 // nsIObserver
286 NS_IMETHODIMP nsExceptionService::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
288 Shutdown();
289 return NS_OK;
292 nsresult
293 nsExceptionService::DoGetExceptionFromProvider(nsresult errCode,
294 nsIException * defaultException,
295 nsIException **_exc)
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
302 if (nr == errCode)
303 return NS_OK;
304 NS_RELEASE(*_exc);
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
311 if (!provider) {
312 *_exc = defaultException;
313 NS_IF_ADDREF(*_exc);
314 return NS_OK;
317 return provider->GetException(errCode, defaultException, _exc);
320 // thread management
321 /*static*/ void nsExceptionService::AddThread(nsExceptionManager *thread)
323 PR_Lock(lock);
324 thread->mNextThread = firstThread;
325 firstThread = thread;
326 NS_ADDREF(thread);
327 PR_Unlock(lock);
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;
338 NS_RELEASE(thread);
341 /*static*/ void nsExceptionService::DropThread(nsExceptionManager *thread)
343 PR_Lock(lock);
344 DoDropThread(thread);
345 PR_Unlock(lock);
348 /*static*/ void nsExceptionService::DropAllThreads()
350 PR_Lock(lock);
351 while (firstThread)
352 DoDropThread(firstThread);
353 PR_Unlock(lock);