diff options
Diffstat (limited to 'src/libs/xpcom18a4/xpcom/base/nsExceptionService.cpp')
-rw-r--r-- | src/libs/xpcom18a4/xpcom/base/nsExceptionService.cpp | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/xpcom/base/nsExceptionService.cpp b/src/libs/xpcom18a4/xpcom/base/nsExceptionService.cpp new file mode 100644 index 00000000..f68f2d5f --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/base/nsExceptionService.cpp @@ -0,0 +1,383 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * ActiveState Tool Corp.. + * Portions created by the Initial Developer are Copyright (C) 2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Hammond <MarkH@ActiveState.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.h" +#include "nsExceptionService.h" +#include "nsIServiceManager.h" +#include "nsCOMPtr.h" +#include "prthread.h" +#include "prlock.h" +static const PRUintn BAD_TLS_INDEX = (PRUintn) -1; + +#define CHECK_SERVICE_USE_OK() if (tlsIndex == BAD_TLS_INDEX) return NS_ERROR_NOT_INITIALIZED +#define CHECK_MANAGER_USE_OK() if (!mService || nsExceptionService::tlsIndex == BAD_TLS_INDEX) return NS_ERROR_NOT_INITIALIZED + +// A key for our registered module providers hashtable +class nsProviderKey : public nsHashKey { +protected: + PRUint32 mKey; +public: + nsProviderKey(PRUint32 key) : mKey(key) {} + PRUint32 HashCode(void) const { + return mKey; + } + PRBool Equals(const nsHashKey *aKey) const { + return mKey == ((const nsProviderKey *) aKey)->mKey; + } + nsHashKey *Clone() const { + return new nsProviderKey(mKey); + } + PRUint32 GetValue() { return mKey; } +}; + +/** Exception Manager definition **/ +class nsExceptionManager : public nsIExceptionManager +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIEXCEPTIONMANAGER + + nsExceptionManager(nsExceptionService *svc); + /* additional members */ + nsCOMPtr<nsIException> mCurrentException; + nsExceptionManager *mNextThread; // not ref-counted. + nsExceptionService *mService; // not ref-counted +#ifdef NS_DEBUG + static PRInt32 totalInstances; +#endif + +#ifdef NS_DEBUG + inline nsrefcnt ReleaseQuiet() { + // shut up NS_ASSERT_OWNINGTHREAD (see explanation below) + nsAutoOwningThread old = _mOwningThread; + _mOwningThread = nsAutoOwningThread(); + nsrefcnt ref = Release(); + NS_ASSERTION(ref == 0, "the object is still referenced by other threads while it shouldn't"); + if (ref != 0) + _mOwningThread = old; + return ref; + } +#else + inline nsrefcnt ReleaseQuiet(void) { return Release(); } +#endif + +private: + ~nsExceptionManager(); +}; + + +#ifdef NS_DEBUG +PRInt32 nsExceptionManager::totalInstances = 0; +#endif + +// Note: the nsExceptionManager object is single threaded - the exception +// service itself ensures one per thread. However, there are two methods that +// may be called on foreign threads: DropAllThreads (called on the thread +// shutting down xpcom) and ThreadDestruct (called after xpcom destroyed the +// internal thread struct, so that PR_GetCurrentThread() will create a new one +// from scratch which will obviously not match the old one stored in the +// instance on creation). In both cases, there should be no other threads +// holding objects (i.e. it's thread-safe to call them), but +// NS_CheckThreadSafe() assertions will still happen and yell in the debug +// build. Since it is quite annoying, we use a special ReleaseQuiet() method +// in DoDropThread() to shut them up. +NS_IMPL_THREADSAFE_ISUPPORTS1(nsExceptionManager, nsIExceptionManager) + +nsExceptionManager::nsExceptionManager(nsExceptionService *svc) : + mNextThread(nsnull), + mService(svc) +{ + /* member initializers and constructor code */ +#ifdef NS_DEBUG + PR_AtomicIncrement(&totalInstances); +#endif +} + +nsExceptionManager::~nsExceptionManager() +{ + /* destructor code */ +#ifdef NS_DEBUG + PR_AtomicDecrement(&totalInstances); +#endif // NS_DEBUG +} + +/* void setCurrentException (in nsIException error); */ +NS_IMETHODIMP nsExceptionManager::SetCurrentException(nsIException *error) +{ + CHECK_MANAGER_USE_OK(); + mCurrentException = error; + return NS_OK; +} + +/* nsIException getCurrentException (); */ +NS_IMETHODIMP nsExceptionManager::GetCurrentException(nsIException **_retval) +{ + CHECK_MANAGER_USE_OK(); + *_retval = mCurrentException; + NS_IF_ADDREF(*_retval); + return NS_OK; +} + +/* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */ +NS_IMETHODIMP nsExceptionManager::GetExceptionFromProvider(nsresult rc, nsIException * defaultException, nsIException **_retval) +{ + CHECK_MANAGER_USE_OK(); + // Just delegate back to the service with the provider map. + return mService->GetExceptionFromProvider(rc, defaultException, _retval); +} + +/* The Exception Service */ + +PRUintn nsExceptionService::tlsIndex = BAD_TLS_INDEX; +PRLock *nsExceptionService::lock = nsnull; +nsExceptionManager *nsExceptionService::firstThread = nsnull; + +#ifdef NS_DEBUG +PRInt32 nsExceptionService::totalInstances = 0; +#endif + +NS_IMPL_THREADSAFE_ISUPPORTS2(nsExceptionService, nsIExceptionService, nsIObserver) + +nsExceptionService::nsExceptionService() + : mProviders(4, PR_TRUE) /* small, thread-safe hashtable */ +{ +#ifdef NS_DEBUG + if (PR_AtomicIncrement(&totalInstances)!=1) { + NS_ERROR("The nsExceptionService is a singleton!"); + } +#endif + /* member initializers and constructor code */ + if (tlsIndex == BAD_TLS_INDEX) { + PRStatus status; + status = PR_NewThreadPrivateIndex( &tlsIndex, ThreadDestruct ); + NS_WARN_IF_FALSE(status==0, "ScriptErrorService could not allocate TLS storage."); + } + lock = PR_NewLock(); + NS_WARN_IF_FALSE(lock, "Error allocating ExceptionService lock"); + + // observe XPCOM shutdown. + nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1"); + NS_WARN_IF_FALSE(observerService, "Could not get observer service!"); + if (observerService) + observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE); +} + +nsExceptionService::~nsExceptionService() +{ + Shutdown(); + if (lock) { + PRLock *tmp = lock; + lock = nsnull; + PR_DestroyLock(tmp); + } + /* destructor code */ +#ifdef NS_DEBUG + PR_AtomicDecrement(&totalInstances); +#endif +} + +/*static*/ +void nsExceptionService::ThreadDestruct( void *data ) +{ + if (!lock) { + NS_WARNING("nsExceptionService ignoring thread destruction after shutdown"); + return; + } + DropThread( (nsExceptionManager *)data ); +} + + +void nsExceptionService::Shutdown() +{ + PRUintn tmp = tlsIndex; + tlsIndex = BAD_TLS_INDEX; + PR_SetThreadPrivate(tmp, nsnull); + mProviders.Reset(); + if (lock) { + DropAllThreads(); + } +} + +/* void setCurrentException (in nsIException error); */ +NS_IMETHODIMP nsExceptionService::SetCurrentException(nsIException *error) +{ + CHECK_SERVICE_USE_OK(); + nsCOMPtr<nsIExceptionManager> sm; + nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm)); + if (NS_FAILED(nr)) + return nr; + return sm->SetCurrentException(error); +} + +/* nsIException getCurrentException (); */ +NS_IMETHODIMP nsExceptionService::GetCurrentException(nsIException **_retval) +{ + CHECK_SERVICE_USE_OK(); + nsCOMPtr<nsIExceptionManager> sm; + nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm)); + if (NS_FAILED(nr)) + return nr; + return sm->GetCurrentException(_retval); +} + +/* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */ +NS_IMETHODIMP nsExceptionService::GetExceptionFromProvider(nsresult rc, + nsIException * defaultException, nsIException **_retval) +{ + CHECK_SERVICE_USE_OK(); + return DoGetExceptionFromProvider(rc, defaultException, _retval); +} + +/* readonly attribute nsIExceptionManager currentExceptionManager; */ +NS_IMETHODIMP nsExceptionService::GetCurrentExceptionManager(nsIExceptionManager * *aCurrentScriptManager) +{ + CHECK_SERVICE_USE_OK(); + nsExceptionManager *mgr = (nsExceptionManager *)PR_GetThreadPrivate(tlsIndex); + if (mgr == nsnull) { + // Stick the new exception object in with no reference count. + mgr = new nsExceptionManager(this); + if (mgr == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + PR_SetThreadPrivate(tlsIndex, mgr); + // The reference count is held in the thread-list + AddThread(mgr); + } + *aCurrentScriptManager = mgr; + NS_ADDREF(*aCurrentScriptManager); + return NS_OK; +} + +/* void registerErrorProvider (in nsIExceptionProvider provider, in PRUint32 moduleCode); */ +NS_IMETHODIMP nsExceptionService::RegisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule) +{ + CHECK_SERVICE_USE_OK(); + + nsProviderKey key(errorModule); + if (mProviders.Put(&key, provider)) { + NS_WARNING("Registration of exception provider overwrote another provider with the same module code!"); + } + return NS_OK; +} + +/* void unregisterErrorProvider (in nsIExceptionProvider provider, in PRUint32 errorModule); */ +NS_IMETHODIMP nsExceptionService::UnregisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule) +{ + CHECK_SERVICE_USE_OK(); + nsProviderKey key(errorModule); + if (!mProviders.Remove(&key)) { + NS_WARNING("Attempt to unregister an unregistered exception provider!"); + return NS_ERROR_UNEXPECTED; + } + return NS_OK; +} + +// nsIObserver +NS_IMETHODIMP nsExceptionService::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData) +{ + Shutdown(); + return NS_OK; +} + +nsresult +nsExceptionService::DoGetExceptionFromProvider(nsresult errCode, + nsIException * defaultException, + nsIException **_exc) +{ + // Check for an existing exception + nsresult nr = GetCurrentException(_exc); + if (NS_SUCCEEDED(nr) && *_exc) { + (*_exc)->GetResult(&nr); + // If it matches our result then use it + if (nr == errCode) + return NS_OK; + NS_RELEASE(*_exc); + } + nsProviderKey key(NS_ERROR_GET_MODULE(errCode)); + nsCOMPtr<nsIExceptionProvider> provider = + dont_AddRef((nsIExceptionProvider *)mProviders.Get(&key)); + + // No provider so we'll return the default exception + if (!provider) { + *_exc = defaultException; + NS_IF_ADDREF(*_exc); + return NS_OK; + } + + return provider->GetException(errCode, defaultException, _exc); +} + +// thread management +/*static*/ void nsExceptionService::AddThread(nsExceptionManager *thread) +{ + PR_Lock(lock); + thread->mNextThread = firstThread; + firstThread = thread; + NS_ADDREF(thread); + PR_Unlock(lock); +} + +/*static*/ void nsExceptionService::DoDropThread(nsExceptionManager *thread) +{ + nsExceptionManager **emp = &firstThread; + while (*emp != thread) { + if (!*emp) + { + NS_WARNING("Could not find the thread to drop!"); + return; + } + emp = &(*emp)->mNextThread; + } + *emp = thread->mNextThread; + thread->ReleaseQuiet(); + thread = nsnull; +} + +/*static*/ void nsExceptionService::DropThread(nsExceptionManager *thread) +{ + PR_Lock(lock); + DoDropThread(thread); + PR_Unlock(lock); +} + +/*static*/ void nsExceptionService::DropAllThreads() +{ + PR_Lock(lock); + while (firstThread) + DoDropThread(firstThread); + PR_Unlock(lock); +} |