VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/base/nsExceptionService.cpp@ 102103

Last change on this file since 102103 was 101957, checked in by vboxsync, 15 months ago

libs/xpcom: Convert nsExceptionService.{cpp,h} to IPRT, bugref:10545

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.2 KB
Line 
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 <[email protected]>
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 "nsISupports.h"
40#include "nsExceptionService.h"
41#include "nsIServiceManager.h"
42#include "nsCOMPtr.h"
43#include "prthread.h"
44#include "prlock.h"
45
46#include <iprt/asm.h>
47#include <iprt/errcore.h>
48
49#define CHECK_SERVICE_USE_OK() if (tlsIndex == NIL_RTTLS) return NS_ERROR_NOT_INITIALIZED
50#define CHECK_MANAGER_USE_OK() if (!mService || nsExceptionService::tlsIndex == NIL_RTTLS) return NS_ERROR_NOT_INITIALIZED
51
52// A key for our registered module providers hashtable
53class nsProviderKey : public nsHashKey {
54protected:
55 PRUint32 mKey;
56public:
57 nsProviderKey(PRUint32 key) : mKey(key) {}
58 PRUint32 HashCode(void) const {
59 return mKey;
60 }
61 PRBool Equals(const nsHashKey *aKey) const {
62 return mKey == ((const nsProviderKey *) aKey)->mKey;
63 }
64 nsHashKey *Clone() const {
65 return new nsProviderKey(mKey);
66 }
67 PRUint32 GetValue() { return mKey; }
68};
69
70/** Exception Manager definition **/
71class nsExceptionManager : public nsIExceptionManager
72{
73public:
74 NS_DECL_ISUPPORTS
75 NS_DECL_NSIEXCEPTIONMANAGER
76
77 nsExceptionManager(nsExceptionService *svc);
78 /* additional members */
79 nsCOMPtr<nsIException> mCurrentException;
80 nsExceptionManager *mNextThread; // not ref-counted.
81 nsExceptionService *mService; // not ref-counted
82#ifdef NS_DEBUG
83 static volatile uint32_t totalInstances;
84#endif
85
86#ifdef NS_DEBUG
87 inline nsrefcnt ReleaseQuiet() {
88 // shut up NS_ASSERT_OWNINGTHREAD (see explanation below)
89 nsAutoOwningThread old = _mOwningThread;
90 _mOwningThread = nsAutoOwningThread();
91 nsrefcnt ref = Release();
92 NS_ASSERTION(ref == 0, "the object is still referenced by other threads while it shouldn't");
93 if (ref != 0)
94 _mOwningThread = old;
95 return ref;
96 }
97#else
98 inline nsrefcnt ReleaseQuiet(void) { return Release(); }
99#endif
100
101private:
102 ~nsExceptionManager();
103};
104
105
106#ifdef NS_DEBUG
107volatile uint32_t nsExceptionManager::totalInstances = 0;
108#endif
109
110// Note: the nsExceptionManager object is single threaded - the exception
111// service itself ensures one per thread. However, there are two methods that
112// may be called on foreign threads: DropAllThreads (called on the thread
113// shutting down xpcom) and ThreadDestruct (called after xpcom destroyed the
114// internal thread struct, so that PR_GetCurrentThread() will create a new one
115// from scratch which will obviously not match the old one stored in the
116// instance on creation). In both cases, there should be no other threads
117// holding objects (i.e. it's thread-safe to call them), but
118// NS_CheckThreadSafe() assertions will still happen and yell in the debug
119// build. Since it is quite annoying, we use a special ReleaseQuiet() method
120// in DoDropThread() to shut them up.
121NS_IMPL_THREADSAFE_ISUPPORTS1(nsExceptionManager, nsIExceptionManager)
122
123nsExceptionManager::nsExceptionManager(nsExceptionService *svc) :
124 mNextThread(nsnull),
125 mService(svc)
126{
127 /* member initializers and constructor code */
128#ifdef NS_DEBUG
129 ASMAtomicIncU32(&totalInstances);
130#endif
131}
132
133nsExceptionManager::~nsExceptionManager()
134{
135 /* destructor code */
136#ifdef NS_DEBUG
137 ASMAtomicDecU32(&totalInstances);
138#endif // NS_DEBUG
139}
140
141/* void setCurrentException (in nsIException error); */
142NS_IMETHODIMP nsExceptionManager::SetCurrentException(nsIException *error)
143{
144 CHECK_MANAGER_USE_OK();
145 mCurrentException = error;
146 return NS_OK;
147}
148
149/* nsIException getCurrentException (); */
150NS_IMETHODIMP nsExceptionManager::GetCurrentException(nsIException **_retval)
151{
152 CHECK_MANAGER_USE_OK();
153 *_retval = mCurrentException;
154 NS_IF_ADDREF(*_retval);
155 return NS_OK;
156}
157
158/* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
159NS_IMETHODIMP nsExceptionManager::GetExceptionFromProvider(nsresult rc, nsIException * defaultException, nsIException **_retval)
160{
161 CHECK_MANAGER_USE_OK();
162 // Just delegate back to the service with the provider map.
163 return mService->GetExceptionFromProvider(rc, defaultException, _retval);
164}
165
166/* The Exception Service */
167
168RTTLS nsExceptionService::tlsIndex = NIL_RTTLS;
169RTSEMFASTMUTEX nsExceptionService::lock = NIL_RTSEMFASTMUTEX;
170nsExceptionManager *nsExceptionService::firstThread = nsnull;
171
172#ifdef NS_DEBUG
173volatile uint32_t nsExceptionService::totalInstances = 0;
174#endif
175
176NS_IMPL_THREADSAFE_ISUPPORTS2(nsExceptionService, nsIExceptionService, nsIObserver)
177
178nsExceptionService::nsExceptionService()
179 : mProviders(4, PR_TRUE) /* small, thread-safe hashtable */
180{
181#ifdef NS_DEBUG
182 if (ASMAtomicIncU32(&totalInstances)!=1) {
183 NS_ERROR("The nsExceptionService is a singleton!");
184 }
185#endif
186 /* member initializers and constructor code */
187 if (tlsIndex == NIL_RTTLS) {
188 int vrc = RTTlsAllocEx( &tlsIndex, ThreadDestruct );
189 NS_WARN_IF_FALSE(RT_SUCCESS(vrc), "ScriptErrorService could not allocate TLS storage.");
190 }
191 int vrc = RTSemFastMutexCreate(&lock);
192 NS_WARN_IF_FALSE(RT_SUCCESS(vrc), "Error allocating ExceptionService lock");
193
194 // observe XPCOM shutdown.
195 nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1");
196 NS_WARN_IF_FALSE(observerService, "Could not get observer service!");
197 if (observerService)
198 observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
199}
200
201nsExceptionService::~nsExceptionService()
202{
203 Shutdown();
204 if (lock != NIL_RTSEMFASTMUTEX) {
205 RTSEMFASTMUTEX tmp = lock;
206 lock = NULL;
207 RTSemFastMutexDestroy(tmp);
208 }
209 /* destructor code */
210#ifdef NS_DEBUG
211 ASMAtomicDecU32(&totalInstances);
212#endif
213}
214
215/*static*/
216DECLCALLBACK(void) nsExceptionService::ThreadDestruct( void *data )
217{
218 if (lock == NIL_RTSEMFASTMUTEX) {
219 NS_WARNING("nsExceptionService ignoring thread destruction after shutdown");
220 return;
221 }
222 DropThread( (nsExceptionManager *)data );
223}
224
225
226void nsExceptionService::Shutdown()
227{
228 RTTLS tmp = tlsIndex;
229 tlsIndex = NIL_RTTLS;
230 RTTlsSet(tmp, NULL);
231 mProviders.Reset();
232 if (lock != NIL_RTSEMFASTMUTEX) {
233 DropAllThreads();
234 }
235}
236
237/* void setCurrentException (in nsIException error); */
238NS_IMETHODIMP nsExceptionService::SetCurrentException(nsIException *error)
239{
240 CHECK_SERVICE_USE_OK();
241 nsCOMPtr<nsIExceptionManager> sm;
242 nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm));
243 if (NS_FAILED(nr))
244 return nr;
245 return sm->SetCurrentException(error);
246}
247
248/* nsIException getCurrentException (); */
249NS_IMETHODIMP nsExceptionService::GetCurrentException(nsIException **_retval)
250{
251 CHECK_SERVICE_USE_OK();
252 nsCOMPtr<nsIExceptionManager> sm;
253 nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm));
254 if (NS_FAILED(nr))
255 return nr;
256 return sm->GetCurrentException(_retval);
257}
258
259/* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
260NS_IMETHODIMP nsExceptionService::GetExceptionFromProvider(nsresult rc,
261 nsIException * defaultException, nsIException **_retval)
262{
263 CHECK_SERVICE_USE_OK();
264 return DoGetExceptionFromProvider(rc, defaultException, _retval);
265}
266
267/* readonly attribute nsIExceptionManager currentExceptionManager; */
268NS_IMETHODIMP nsExceptionService::GetCurrentExceptionManager(nsIExceptionManager * *aCurrentScriptManager)
269{
270 CHECK_SERVICE_USE_OK();
271 nsExceptionManager *mgr = (nsExceptionManager *)RTTlsGet(tlsIndex);
272 if (mgr == nsnull) {
273 // Stick the new exception object in with no reference count.
274 mgr = new nsExceptionManager(this);
275 if (mgr == nsnull)
276 return NS_ERROR_OUT_OF_MEMORY;
277 RTTlsSet(tlsIndex, mgr);
278 // The reference count is held in the thread-list
279 AddThread(mgr);
280 }
281 *aCurrentScriptManager = mgr;
282 NS_ADDREF(*aCurrentScriptManager);
283 return NS_OK;
284}
285
286/* void registerErrorProvider (in nsIExceptionProvider provider, in PRUint32 moduleCode); */
287NS_IMETHODIMP nsExceptionService::RegisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule)
288{
289 CHECK_SERVICE_USE_OK();
290
291 nsProviderKey key(errorModule);
292 if (mProviders.Put(&key, provider)) {
293 NS_WARNING("Registration of exception provider overwrote another provider with the same module code!");
294 }
295 return NS_OK;
296}
297
298/* void unregisterErrorProvider (in nsIExceptionProvider provider, in PRUint32 errorModule); */
299NS_IMETHODIMP nsExceptionService::UnregisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule)
300{
301 CHECK_SERVICE_USE_OK();
302 nsProviderKey key(errorModule);
303 if (!mProviders.Remove(&key)) {
304 NS_WARNING("Attempt to unregister an unregistered exception provider!");
305 return NS_ERROR_UNEXPECTED;
306 }
307 return NS_OK;
308}
309
310// nsIObserver
311NS_IMETHODIMP nsExceptionService::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
312{
313 Shutdown();
314 return NS_OK;
315}
316
317nsresult
318nsExceptionService::DoGetExceptionFromProvider(nsresult errCode,
319 nsIException * defaultException,
320 nsIException **_exc)
321{
322 // Check for an existing exception
323 nsresult nr = GetCurrentException(_exc);
324 if (NS_SUCCEEDED(nr) && *_exc) {
325 (*_exc)->GetResult(&nr);
326 // If it matches our result then use it
327 if (nr == errCode)
328 return NS_OK;
329 NS_RELEASE(*_exc);
330 }
331 nsProviderKey key(NS_ERROR_GET_MODULE(errCode));
332 nsCOMPtr<nsIExceptionProvider> provider =
333 dont_AddRef((nsIExceptionProvider *)mProviders.Get(&key));
334
335 // No provider so we'll return the default exception
336 if (!provider) {
337 *_exc = defaultException;
338 NS_IF_ADDREF(*_exc);
339 return NS_OK;
340 }
341
342 return provider->GetException(errCode, defaultException, _exc);
343}
344
345// thread management
346/*static*/ void nsExceptionService::AddThread(nsExceptionManager *thread)
347{
348 RTSemFastMutexRequest(lock);
349 thread->mNextThread = firstThread;
350 firstThread = thread;
351 NS_ADDREF(thread);
352 RTSemFastMutexRelease(lock);
353}
354
355/*static*/ void nsExceptionService::DoDropThread(nsExceptionManager *thread)
356{
357 nsExceptionManager **emp = &firstThread;
358 while (*emp != thread) {
359 if (!*emp)
360 {
361 NS_WARNING("Could not find the thread to drop!");
362 return;
363 }
364 emp = &(*emp)->mNextThread;
365 }
366 *emp = thread->mNextThread;
367 thread->ReleaseQuiet();
368 thread = nsnull;
369}
370
371/*static*/ void nsExceptionService::DropThread(nsExceptionManager *thread)
372{
373 RTSemFastMutexRequest(lock);
374 DoDropThread(thread);
375 RTSemFastMutexRelease(lock);
376}
377
378/*static*/ void nsExceptionService::DropAllThreads()
379{
380 RTSemFastMutexRequest(lock);
381 while (firstThread)
382 DoDropThread(firstThread);
383 RTSemFastMutexRelease(lock);
384}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette