Back to index

lightning-sunbird  0.9+nobinonly
nsExceptionService.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * ActiveState Tool Corp..
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Mark Hammond <MarkH@ActiveState.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsISupports.h"
00040 #include "nsExceptionService.h"
00041 #include "nsIServiceManager.h"
00042 #include "nsCOMPtr.h"
00043 #include "prthread.h"
00044 #include "prlock.h"
00045 static const PRUintn BAD_TLS_INDEX = (PRUintn) -1;
00046 
00047 #define CHECK_SERVICE_USE_OK() if (!lock) return NS_ERROR_NOT_INITIALIZED
00048 #define CHECK_MANAGER_USE_OK() if (!mService || !nsExceptionService::lock) return NS_ERROR_NOT_INITIALIZED
00049 
00050 // A key for our registered module providers hashtable
00051 class nsProviderKey : public nsHashKey {
00052 protected:
00053   PRUint32 mKey;
00054 public:
00055   nsProviderKey(PRUint32 key) : mKey(key) {}
00056   PRUint32 HashCode(void) const {
00057     return mKey;
00058   }
00059   PRBool Equals(const nsHashKey *aKey) const {
00060     return mKey == ((const nsProviderKey *) aKey)->mKey;
00061   }
00062   nsHashKey *Clone() const {
00063     return new nsProviderKey(mKey);
00064   }
00065   PRUint32 GetValue() { return mKey; }
00066 };
00067 
00069 class nsExceptionManager : public nsIExceptionManager
00070 {
00071 public:
00072   NS_DECL_ISUPPORTS
00073   NS_DECL_NSIEXCEPTIONMANAGER
00074 
00075   nsExceptionManager(nsExceptionService *svc);
00076   /* additional members */
00077   nsCOMPtr<nsIException> mCurrentException;
00078   nsExceptionManager *mNextThread; // not ref-counted.
00079   nsExceptionService *mService; // not ref-counted
00080 #ifdef NS_DEBUG
00081   static PRInt32 totalInstances;
00082 #endif
00083 
00084 private:
00085   ~nsExceptionManager();
00086 };
00087 
00088 
00089 #ifdef NS_DEBUG
00090 PRInt32 nsExceptionManager::totalInstances = 0;
00091 #endif
00092 
00093 // Note this object is single threaded - the service itself ensures
00094 // one per thread.
00095 // An exception if the destructor, which may be called on
00096 // the thread shutting down xpcom
00097 NS_IMPL_ISUPPORTS1(nsExceptionManager, nsIExceptionManager)
00098 
00099 nsExceptionManager::nsExceptionManager(nsExceptionService *svc) :
00100   mNextThread(nsnull),
00101   mService(svc)
00102 {
00103   /* member initializers and constructor code */
00104 #ifdef NS_DEBUG
00105   PR_AtomicIncrement(&totalInstances);
00106 #endif
00107 }
00108 
00109 nsExceptionManager::~nsExceptionManager()
00110 {
00111   /* destructor code */
00112 #ifdef NS_DEBUG
00113   PR_AtomicDecrement(&totalInstances);
00114 #endif // NS_DEBUG
00115 }
00116 
00117 /* void setCurrentException (in nsIException error); */
00118 NS_IMETHODIMP nsExceptionManager::SetCurrentException(nsIException *error)
00119 {
00120     CHECK_MANAGER_USE_OK();
00121     mCurrentException = error;
00122     return NS_OK;
00123 }
00124 
00125 /* nsIException getCurrentException (); */
00126 NS_IMETHODIMP nsExceptionManager::GetCurrentException(nsIException **_retval)
00127 {
00128     CHECK_MANAGER_USE_OK();
00129     *_retval = mCurrentException;
00130     NS_IF_ADDREF(*_retval);
00131     return NS_OK;
00132 }
00133 
00134 /* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
00135 NS_IMETHODIMP nsExceptionManager::GetExceptionFromProvider(nsresult rc, nsIException * defaultException, nsIException **_retval)
00136 {
00137     CHECK_MANAGER_USE_OK();
00138     // Just delegate back to the service with the provider map.
00139     return mService->GetExceptionFromProvider(rc, defaultException, _retval);
00140 }
00141 
00142 /* The Exception Service */
00143 
00144 PRUintn nsExceptionService::tlsIndex = BAD_TLS_INDEX;
00145 PRLock *nsExceptionService::lock = nsnull;
00146 nsExceptionManager *nsExceptionService::firstThread = nsnull;
00147 
00148 #ifdef NS_DEBUG
00149 PRInt32 nsExceptionService::totalInstances = 0;
00150 #endif
00151 
00152 NS_IMPL_THREADSAFE_ISUPPORTS2(nsExceptionService, nsIExceptionService, nsIObserver)
00153 
00154 nsExceptionService::nsExceptionService()
00155   : mProviders(4, PR_TRUE) /* small, thread-safe hashtable */
00156 {
00157 #ifdef NS_DEBUG
00158   if (PR_AtomicIncrement(&totalInstances)!=1) {
00159     NS_ERROR("The nsExceptionService is a singleton!");
00160   }
00161 #endif
00162   /* member initializers and constructor code */
00163   if (tlsIndex == BAD_TLS_INDEX) {
00164     PRStatus status;
00165     status = PR_NewThreadPrivateIndex( &tlsIndex, ThreadDestruct );
00166     NS_WARN_IF_FALSE(status==0, "ScriptErrorService could not allocate TLS storage.");
00167   }
00168   lock = PR_NewLock();
00169   NS_WARN_IF_FALSE(lock, "Error allocating ExceptionService lock");
00170 
00171   // observe XPCOM shutdown.
00172   nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1");
00173   NS_WARN_IF_FALSE(observerService, "Could not get observer service!");
00174   if (observerService)
00175     observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
00176 }
00177 
00178 nsExceptionService::~nsExceptionService()
00179 {
00180   Shutdown();
00181   /* destructor code */
00182 #ifdef NS_DEBUG
00183   PR_AtomicDecrement(&totalInstances);
00184 #endif
00185 }
00186 
00187 /*static*/
00188 void nsExceptionService::ThreadDestruct( void *data )
00189 {
00190   if (!lock) {
00191     NS_WARNING("nsExceptionService ignoring thread destruction after shutdown");
00192     return;
00193   }
00194   DropThread( (nsExceptionManager *)data );
00195 }
00196 
00197 
00198 void nsExceptionService::Shutdown()
00199 {
00200   mProviders.Reset();
00201   if (lock) {
00202     DropAllThreads();
00203     PR_DestroyLock(lock);
00204     lock = nsnull;
00205   }
00206   PR_SetThreadPrivate(tlsIndex, nsnull);
00207 }
00208 
00209 /* void setCurrentException (in nsIException error); */
00210 NS_IMETHODIMP nsExceptionService::SetCurrentException(nsIException *error)
00211 {
00212     CHECK_SERVICE_USE_OK();
00213     nsCOMPtr<nsIExceptionManager> sm;
00214     nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm));
00215     if (NS_FAILED(nr))
00216         return nr;
00217     return sm->SetCurrentException(error);
00218 }
00219 
00220 /* nsIException getCurrentException (); */
00221 NS_IMETHODIMP nsExceptionService::GetCurrentException(nsIException **_retval)
00222 {
00223     CHECK_SERVICE_USE_OK();
00224     nsCOMPtr<nsIExceptionManager> sm;
00225     nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm));
00226     if (NS_FAILED(nr))
00227         return nr;
00228     return sm->GetCurrentException(_retval);
00229 }
00230 
00231 /* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
00232 NS_IMETHODIMP nsExceptionService::GetExceptionFromProvider(nsresult rc, 
00233     nsIException * defaultException, nsIException **_retval)
00234 {
00235     CHECK_SERVICE_USE_OK();
00236     return DoGetExceptionFromProvider(rc, defaultException, _retval);
00237 }
00238 
00239 /* readonly attribute nsIExceptionManager currentExceptionManager; */
00240 NS_IMETHODIMP nsExceptionService::GetCurrentExceptionManager(nsIExceptionManager * *aCurrentScriptManager)
00241 {
00242     CHECK_SERVICE_USE_OK();
00243     nsExceptionManager *mgr = (nsExceptionManager *)PR_GetThreadPrivate(tlsIndex);
00244     if (mgr == nsnull) {
00245         // Stick the new exception object in with no reference count.
00246         mgr = new nsExceptionManager(this);
00247         if (mgr == nsnull)
00248             return NS_ERROR_OUT_OF_MEMORY;
00249         PR_SetThreadPrivate(tlsIndex, mgr);
00250         // The reference count is held in the thread-list
00251         AddThread(mgr);
00252     }
00253     *aCurrentScriptManager = mgr;
00254     NS_ADDREF(*aCurrentScriptManager);
00255     return NS_OK;
00256 }
00257 
00258 /* void registerErrorProvider (in nsIExceptionProvider provider, in PRUint32 moduleCode); */
00259 NS_IMETHODIMP nsExceptionService::RegisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule)
00260 {
00261     CHECK_SERVICE_USE_OK();
00262 
00263     nsProviderKey key(errorModule);
00264     if (mProviders.Put(&key, provider)) {
00265         NS_WARNING("Registration of exception provider overwrote another provider with the same module code!");
00266     }
00267     return NS_OK;
00268 }
00269 
00270 /* void unregisterErrorProvider (in nsIExceptionProvider provider, in PRUint32 errorModule); */
00271 NS_IMETHODIMP nsExceptionService::UnregisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule)
00272 {
00273     CHECK_SERVICE_USE_OK();
00274     nsProviderKey key(errorModule);
00275     if (!mProviders.Remove(&key)) {
00276         NS_WARNING("Attempt to unregister an unregistered exception provider!");
00277         return NS_ERROR_UNEXPECTED;
00278     }
00279     return NS_OK;
00280 }
00281 
00282 // nsIObserver
00283 NS_IMETHODIMP nsExceptionService::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
00284 {
00285      Shutdown();
00286      return NS_OK;
00287 }
00288 
00289 nsresult
00290 nsExceptionService::DoGetExceptionFromProvider(nsresult errCode, 
00291                                                nsIException * defaultException,
00292                                                nsIException **_exc)
00293 {
00294     // Check for an existing exception
00295     nsresult nr = GetCurrentException(_exc);
00296     if (NS_SUCCEEDED(nr) && *_exc) {
00297         (*_exc)->GetResult(&nr);
00298         // If it matches our result then use it
00299         if (nr == errCode)
00300             return NS_OK;
00301         NS_RELEASE(*_exc);
00302     }
00303     nsProviderKey key(NS_ERROR_GET_MODULE(errCode));
00304     nsCOMPtr<nsIExceptionProvider> provider =
00305         dont_AddRef((nsIExceptionProvider *)mProviders.Get(&key));
00306 
00307     // No provider so we'll return the default exception
00308     if (!provider) {
00309         *_exc = defaultException;
00310         NS_IF_ADDREF(*_exc);
00311         return NS_OK;
00312     }
00313 
00314     return provider->GetException(errCode, defaultException, _exc);
00315 }
00316 
00317 // thread management
00318 /*static*/ void nsExceptionService::AddThread(nsExceptionManager *thread)
00319 {
00320     PR_Lock(lock);
00321     thread->mNextThread = firstThread;
00322     firstThread = thread;
00323     NS_ADDREF(thread);
00324     PR_Unlock(lock);
00325 }
00326 
00327 /*static*/ void nsExceptionService::DoDropThread(nsExceptionManager *thread)
00328 {
00329     nsExceptionManager **emp = &firstThread;
00330     while (*emp != thread) {
00331         NS_ABORT_IF_FALSE(*emp, "Could not find the thread to drop!");
00332         emp = &(*emp)->mNextThread;
00333     }
00334     *emp = thread->mNextThread;
00335     NS_RELEASE(thread);
00336 }
00337 
00338 /*static*/ void nsExceptionService::DropThread(nsExceptionManager *thread)
00339 {
00340     PR_Lock(lock);
00341     DoDropThread(thread);
00342     PR_Unlock(lock);
00343 }
00344 
00345 /*static*/ void nsExceptionService::DropAllThreads()
00346 {
00347     PR_Lock(lock);
00348     while (firstThread)
00349         DoDropThread(firstThread);
00350     PR_Unlock(lock);
00351 }