VirtualBox

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

Last change on this file since 58038 was 56585, checked in by vboxsync, 9 years ago

xpcom: Make nsExceptionManager use thread safe reference counting. The class at such is single threaded and there is one manager per thread but nsExceptionService::DropAllThreads() can be called from other threads as explained in the comment slightly above

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