pjs/xpcom/base/nsExceptionService.cpp

329 строки
9.5 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 ActiveState are Copyright (C) 2001 ActiveState
* Tool Corporation. All Rights Reserved.
*
* Contributor(s): Mark Hammond <MarkH@ActiveState.com>
*/
#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 (!lock) return NS_ERROR_NOT_INITIALIZED
#define CHECK_MANAGER_USE_OK() if (!mService || !nsExceptionService::lock) 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);
virtual ~nsExceptionManager();
/* 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
PRInt32 nsExceptionManager::totalInstances = 0;
#endif
// Note this object is single threaded - the service itself ensures
// one per thread.
// An exception if the destructor, which may be called on
// the thread shutting down xpcom
NS_IMPL_ISUPPORTS1(nsExceptionManager, nsIExceptionManager)
nsExceptionManager::nsExceptionManager(nsExceptionService *svc) :
mNextThread(nsnull),
mService(svc)
{
/* member initializers and constructor code */
NS_INIT_ISUPPORTS();
#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); */
NS_IMETHODIMP nsExceptionManager::GetExceptionFromProvider(nsresult rc, nsIException **_retval)
{
CHECK_MANAGER_USE_OK();
// Just delegate back to the service with the provider map.
return mService->GetExceptionFromProvider(rc, _retval);
}
/* The Exception Service */
class ProviderInfo {
public:
ProviderInfo(nsIExceptionProvider *_rep, PRUint32 _mod) :
provider(_rep), errorModule(_mod) {}
nsCOMPtr<nsIExceptionProvider> provider;
PRUint32 errorModule;
};
PRUintn nsExceptionService::tlsIndex = BAD_TLS_INDEX;
PRLock *nsExceptionService::lock = PR_FALSE;
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
NS_INIT_ISUPPORTS();
/* 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(NS_OBSERVERSERVICE_CONTRACTID);
NS_WARN_IF_FALSE(observerService, "Could not get observer service!");
if (observerService)
observerService->AddObserver(this, NS_LITERAL_STRING(NS_XPCOM_SHUTDOWN_OBSERVER_ID).get());
}
nsExceptionService::~nsExceptionService()
{
Shutdown();
/* 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()
{
mProviders.Reset();
if (lock) {
DropAllThreads();
PR_DestroyLock(lock);
lock = nsnull;
}
}
/* 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); */
NS_IMETHODIMP nsExceptionService::GetExceptionFromProvider(nsresult rc, nsIException **_retval)
{
CHECK_SERVICE_USE_OK();
return DoGetExceptionFromProvider(rc, _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();
ProviderInfo *pi = new ProviderInfo(provider, errorModule);
if (pi==nsnull)
return NS_ERROR_OUT_OF_MEMORY;
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 PRUnichar *aTopic, const PRUnichar *someData)
{
Shutdown();
return NS_OK;
}
nsresult nsExceptionService::DoGetExceptionFromProvider( nsresult errCode, nsIException **_exc)
{
nsProviderKey key(NS_ERROR_GET_MODULE(errCode));
nsCOMPtr<nsIExceptionProvider> provider((nsIExceptionProvider *)mProviders.Get(&key));
if (!provider) {
*_exc = nsnull;
return NS_COMFALSE;
}
return provider->GetException(errCode, _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) {
NS_ABORT_IF_FALSE(*emp, "Could not find the thread to drop!");
emp = &(*emp)->mNextThread;
}
*emp = thread->mNextThread;
NS_RELEASE(thread);
}
/*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);
}