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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : * Mark Hammond <MarkH@ActiveState.com>
24 : *
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.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "mozilla/Attributes.h"
40 :
41 : #include "nsISupports.h"
42 : #include "nsExceptionService.h"
43 : #include "nsIServiceManager.h"
44 : #include "nsCOMPtr.h"
45 : #include "prthread.h"
46 : #include "mozilla/Services.h"
47 :
48 : using namespace mozilla;
49 :
50 : static const PRUintn BAD_TLS_INDEX = (PRUintn) -1;
51 :
52 : #define CHECK_SERVICE_USE_OK() if (!sLock) return NS_ERROR_NOT_INITIALIZED
53 : #define CHECK_MANAGER_USE_OK() if (!mService || !nsExceptionService::sLock) return NS_ERROR_NOT_INITIALIZED
54 :
55 : // A key for our registered module providers hashtable
56 78436 : class nsProviderKey : public nsHashKey {
57 : protected:
58 : PRUint32 mKey;
59 : public:
60 38918 : nsProviderKey(PRUint32 key) : mKey(key) {}
61 38618 : PRUint32 HashCode(void) const {
62 38618 : return mKey;
63 : }
64 467 : bool Equals(const nsHashKey *aKey) const {
65 467 : return mKey == ((const nsProviderKey *) aKey)->mKey;
66 : }
67 300 : nsHashKey *Clone() const {
68 300 : return new nsProviderKey(mKey);
69 : }
70 : PRUint32 GetValue() { return mKey; }
71 : };
72 :
73 : /** Exception Manager definition **/
74 : class nsExceptionManager MOZ_FINAL : public nsIExceptionManager
75 : {
76 : public:
77 : NS_DECL_ISUPPORTS
78 : NS_DECL_NSIEXCEPTIONMANAGER
79 :
80 : nsExceptionManager(nsExceptionService *svc);
81 : /* additional members */
82 : nsCOMPtr<nsIException> mCurrentException;
83 : nsExceptionManager *mNextThread; // not ref-counted.
84 : nsExceptionService *mService; // not ref-counted
85 : #ifdef NS_DEBUG
86 : static PRInt32 totalInstances;
87 : #endif
88 :
89 : private:
90 : ~nsExceptionManager();
91 : };
92 :
93 :
94 : #ifdef NS_DEBUG
95 : PRInt32 nsExceptionManager::totalInstances = 0;
96 : #endif
97 :
98 : // Note this object is single threaded - the service itself ensures
99 : // one per thread.
100 : // An exception if the destructor, which may be called on
101 : // the thread shutting down xpcom
102 197308 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsExceptionManager, nsIExceptionManager)
103 :
104 1391 : nsExceptionManager::nsExceptionManager(nsExceptionService *svc) :
105 : mNextThread(nsnull),
106 1391 : mService(svc)
107 : {
108 : /* member initializers and constructor code */
109 : #ifdef NS_DEBUG
110 1391 : PR_ATOMIC_INCREMENT(&totalInstances);
111 : #endif
112 1391 : }
113 :
114 2780 : nsExceptionManager::~nsExceptionManager()
115 : {
116 : /* destructor code */
117 : #ifdef NS_DEBUG
118 1390 : PR_ATOMIC_DECREMENT(&totalInstances);
119 : #endif // NS_DEBUG
120 1390 : }
121 :
122 : /* void setCurrentException (in nsIException error); */
123 9203732 : NS_IMETHODIMP nsExceptionManager::SetCurrentException(nsIException *error)
124 : {
125 9203732 : CHECK_MANAGER_USE_OK();
126 9203464 : mCurrentException = error;
127 9203464 : return NS_OK;
128 : }
129 :
130 : /* nsIException getCurrentException (); */
131 76346 : NS_IMETHODIMP nsExceptionManager::GetCurrentException(nsIException **_retval)
132 : {
133 76346 : CHECK_MANAGER_USE_OK();
134 76337 : *_retval = mCurrentException;
135 76337 : NS_IF_ADDREF(*_retval);
136 76337 : return NS_OK;
137 : }
138 :
139 : /* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
140 38077 : NS_IMETHODIMP nsExceptionManager::GetExceptionFromProvider(nsresult rc, nsIException * defaultException, nsIException **_retval)
141 : {
142 38077 : CHECK_MANAGER_USE_OK();
143 : // Just delegate back to the service with the provider map.
144 38068 : return mService->GetExceptionFromProvider(rc, defaultException, _retval);
145 : }
146 :
147 : /* The Exception Service */
148 :
149 : PRUintn nsExceptionService::tlsIndex = BAD_TLS_INDEX;
150 : Mutex *nsExceptionService::sLock = nsnull;
151 : nsExceptionManager *nsExceptionService::firstThread = nsnull;
152 :
153 : #ifdef NS_DEBUG
154 : PRInt32 nsExceptionService::totalInstances = 0;
155 : #endif
156 :
157 22771 : NS_IMPL_THREADSAFE_ISUPPORTS3(nsExceptionService,
158 : nsIExceptionService,
159 : nsIExceptionManager,
160 : nsIObserver)
161 :
162 1391 : nsExceptionService::nsExceptionService()
163 1391 : : mProviders(4, true) /* small, thread-safe hashtable */
164 : {
165 : #ifdef NS_DEBUG
166 1391 : if (PR_ATOMIC_INCREMENT(&totalInstances)!=1) {
167 0 : NS_ERROR("The nsExceptionService is a singleton!");
168 : }
169 : #endif
170 : /* member initializers and constructor code */
171 1391 : if (tlsIndex == BAD_TLS_INDEX) {
172 : PRStatus status;
173 1391 : status = PR_NewThreadPrivateIndex( &tlsIndex, ThreadDestruct );
174 1391 : NS_ASSERTION(status==0, "ScriptErrorService could not allocate TLS storage.");
175 : }
176 1391 : sLock = new Mutex("nsExceptionService.sLock");
177 :
178 : // observe XPCOM shutdown.
179 : nsCOMPtr<nsIObserverService> observerService =
180 2782 : mozilla::services::GetObserverService();
181 1391 : NS_ASSERTION(observerService, "Could not get observer service!");
182 1391 : if (observerService)
183 1391 : observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
184 1391 : }
185 :
186 2782 : nsExceptionService::~nsExceptionService()
187 : {
188 1391 : Shutdown();
189 : /* destructor code */
190 : #ifdef NS_DEBUG
191 1391 : PR_ATOMIC_DECREMENT(&totalInstances);
192 : #endif
193 1391 : }
194 :
195 : /*static*/
196 1391 : void nsExceptionService::ThreadDestruct( void *data )
197 : {
198 1391 : if (!sLock) {
199 1391 : NS_WARNING("nsExceptionService ignoring thread destruction after shutdown");
200 1391 : return;
201 : }
202 0 : DropThread( (nsExceptionManager *)data );
203 : }
204 :
205 :
206 2782 : void nsExceptionService::Shutdown()
207 : {
208 2782 : mProviders.Reset();
209 2782 : if (sLock) {
210 1391 : DropAllThreads();
211 1391 : delete sLock;
212 1391 : sLock = nsnull;
213 : }
214 2782 : PR_SetThreadPrivate(tlsIndex, nsnull);
215 2782 : }
216 :
217 : /* void setCurrentException (in nsIException error); */
218 0 : NS_IMETHODIMP nsExceptionService::SetCurrentException(nsIException *error)
219 : {
220 0 : CHECK_SERVICE_USE_OK();
221 0 : nsCOMPtr<nsIExceptionManager> sm;
222 0 : nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm));
223 0 : if (NS_FAILED(nr))
224 0 : return nr;
225 0 : return sm->SetCurrentException(error);
226 : }
227 :
228 : /* nsIException getCurrentException (); */
229 38068 : NS_IMETHODIMP nsExceptionService::GetCurrentException(nsIException **_retval)
230 : {
231 38068 : CHECK_SERVICE_USE_OK();
232 76136 : nsCOMPtr<nsIExceptionManager> sm;
233 38068 : nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm));
234 38068 : if (NS_FAILED(nr))
235 0 : return nr;
236 38068 : return sm->GetCurrentException(_retval);
237 : }
238 :
239 : /* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
240 38068 : NS_IMETHODIMP nsExceptionService::GetExceptionFromProvider(nsresult rc,
241 : nsIException * defaultException, nsIException **_retval)
242 : {
243 38068 : CHECK_SERVICE_USE_OK();
244 38068 : return DoGetExceptionFromProvider(rc, defaultException, _retval);
245 : }
246 :
247 : /* readonly attribute nsIExceptionManager currentExceptionManager; */
248 39462 : NS_IMETHODIMP nsExceptionService::GetCurrentExceptionManager(nsIExceptionManager * *aCurrentScriptManager)
249 : {
250 39462 : CHECK_SERVICE_USE_OK();
251 39462 : nsExceptionManager *mgr = (nsExceptionManager *)PR_GetThreadPrivate(tlsIndex);
252 39462 : if (mgr == nsnull) {
253 : // Stick the new exception object in with no reference count.
254 1391 : mgr = new nsExceptionManager(this);
255 1391 : PR_SetThreadPrivate(tlsIndex, mgr);
256 : // The reference count is held in the thread-list
257 1391 : AddThread(mgr);
258 : }
259 39462 : *aCurrentScriptManager = mgr;
260 39462 : NS_ADDREF(*aCurrentScriptManager);
261 39462 : return NS_OK;
262 : }
263 :
264 : /* void registerErrorProvider (in nsIExceptionProvider provider, in PRUint32 moduleCode); */
265 300 : NS_IMETHODIMP nsExceptionService::RegisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule)
266 : {
267 300 : CHECK_SERVICE_USE_OK();
268 :
269 600 : nsProviderKey key(errorModule);
270 300 : if (mProviders.Put(&key, provider)) {
271 0 : NS_WARNING("Registration of exception provider overwrote another provider with the same module code!");
272 : }
273 300 : return NS_OK;
274 : }
275 :
276 : /* void unregisterErrorProvider (in nsIExceptionProvider provider, in PRUint32 errorModule); */
277 250 : NS_IMETHODIMP nsExceptionService::UnregisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule)
278 : {
279 250 : CHECK_SERVICE_USE_OK();
280 500 : nsProviderKey key(errorModule);
281 250 : if (!mProviders.Remove(&key)) {
282 0 : NS_WARNING("Attempt to unregister an unregistered exception provider!");
283 0 : return NS_ERROR_UNEXPECTED;
284 : }
285 250 : return NS_OK;
286 : }
287 :
288 : // nsIObserver
289 1391 : NS_IMETHODIMP nsExceptionService::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
290 : {
291 1391 : Shutdown();
292 1391 : return NS_OK;
293 : }
294 :
295 : nsresult
296 38068 : nsExceptionService::DoGetExceptionFromProvider(nsresult errCode,
297 : nsIException * defaultException,
298 : nsIException **_exc)
299 : {
300 : // Check for an existing exception
301 38068 : nsresult nr = GetCurrentException(_exc);
302 38068 : if (NS_SUCCEEDED(nr) && *_exc) {
303 0 : (*_exc)->GetResult(&nr);
304 : // If it matches our result then use it
305 0 : if (nr == errCode)
306 0 : return NS_OK;
307 0 : NS_RELEASE(*_exc);
308 : }
309 76136 : nsProviderKey key(NS_ERROR_GET_MODULE(errCode));
310 : nsCOMPtr<nsIExceptionProvider> provider =
311 76136 : dont_AddRef((nsIExceptionProvider *)mProviders.Get(&key));
312 :
313 : // No provider so we'll return the default exception
314 38068 : if (!provider) {
315 37851 : *_exc = defaultException;
316 37851 : NS_IF_ADDREF(*_exc);
317 37851 : return NS_OK;
318 : }
319 :
320 217 : return provider->GetException(errCode, defaultException, _exc);
321 : }
322 :
323 : // thread management
324 1391 : /*static*/ void nsExceptionService::AddThread(nsExceptionManager *thread)
325 : {
326 2782 : MutexAutoLock lock(*sLock);
327 1391 : thread->mNextThread = firstThread;
328 1391 : firstThread = thread;
329 1391 : NS_ADDREF(thread);
330 1391 : }
331 :
332 1391 : /*static*/ void nsExceptionService::DoDropThread(nsExceptionManager *thread)
333 : {
334 1391 : nsExceptionManager **emp = &firstThread;
335 2782 : while (*emp != thread) {
336 0 : NS_ABORT_IF_FALSE(*emp, "Could not find the thread to drop!");
337 0 : emp = &(*emp)->mNextThread;
338 : }
339 1391 : *emp = thread->mNextThread;
340 1391 : NS_RELEASE(thread);
341 1391 : }
342 :
343 0 : /*static*/ void nsExceptionService::DropThread(nsExceptionManager *thread)
344 : {
345 0 : MutexAutoLock lock(*sLock);
346 0 : DoDropThread(thread);
347 0 : }
348 :
349 1391 : /*static*/ void nsExceptionService::DropAllThreads()
350 : {
351 2782 : MutexAutoLock lock(*sLock);
352 4173 : while (firstThread)
353 1391 : DoDropThread(firstThread);
354 1391 : }
|