VirtualBox

source: vbox/trunk/src/VBox/Main/src-all/VirtualBoxBase.cpp@ 53517

Last change on this file since 53517 was 51903, checked in by vboxsync, 10 years ago

Main: AutoCaller/VirtualBoxBase refactoring, cleanly splitting out the object state handling, and moving all caller synchronization to one file. Also eliminated a not so vital template (AutoCallerBase) by much simpler inheritance. Theoretically has no visible effects, the behavior should be identical. Done as a preparation for reimplementing the caller synchronization.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.0 KB
Line 
1/* $Id: VirtualBoxBase.cpp 51903 2014-07-07 13:03:49Z vboxsync $ */
2
3/** @file
4 *
5 * VirtualBox COM base classes implementation
6 */
7
8/*
9 * Copyright (C) 2006-2014 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20#include <iprt/semaphore.h>
21#include <iprt/asm.h>
22#include <iprt/cpp/exception.h>
23
24#include <typeinfo>
25
26#if !defined(VBOX_WITH_XPCOM)
27#include <windows.h>
28#include <dbghelp.h>
29#else /* !defined(VBOX_WITH_XPCOM) */
30/// @todo remove when VirtualBoxErrorInfo goes away from here
31#include <nsIServiceManager.h>
32#include <nsIExceptionService.h>
33#endif /* !defined(VBOX_WITH_XPCOM) */
34
35#include "VirtualBoxBase.h"
36#include "AutoCaller.h"
37#include "VirtualBoxErrorInfoImpl.h"
38#include "Logging.h"
39
40#include "VBox/com/ErrorInfo.h"
41#include "VBox/com/MultiResult.h"
42
43////////////////////////////////////////////////////////////////////////////////
44//
45// VirtualBoxBase
46//
47////////////////////////////////////////////////////////////////////////////////
48
49VirtualBoxBase::VirtualBoxBase() : mState(this)
50{
51 mObjectLock = NULL;
52}
53
54VirtualBoxBase::~VirtualBoxBase()
55{
56 if (mObjectLock)
57 delete mObjectLock;
58}
59
60/**
61 * This virtual method returns an RWLockHandle that can be used to
62 * protect instance data. This RWLockHandle is generally referred to
63 * as the "object lock"; its locking class (for lock order validation)
64 * must be returned by another virtual method, getLockingClass(), which
65 * by default returns LOCKCLASS_OTHEROBJECT but is overridden by several
66 * subclasses such as VirtualBox, Host, Machine and others.
67 *
68 * On the first call this method lazily creates the RWLockHandle.
69 *
70 * @return
71 */
72/* virtual */
73RWLockHandle *VirtualBoxBase::lockHandle() const
74{
75 /* lazy initialization */
76 if (RT_UNLIKELY(!mObjectLock))
77 {
78 AssertCompile(sizeof(RWLockHandle *) == sizeof(void *));
79
80 // getLockingClass() is overridden by many subclasses to return
81 // one of the locking classes listed at the top of AutoLock.h
82 RWLockHandle *objLock = new RWLockHandle(getLockingClass());
83 if (!ASMAtomicCmpXchgPtr(&mObjectLock, objLock, NULL))
84 {
85 delete objLock;
86 objLock = ASMAtomicReadPtrT(&mObjectLock, RWLockHandle *);
87 }
88 return objLock;
89 }
90 return mObjectLock;
91}
92
93/**
94 * Handles unexpected exceptions by turning them into COM errors in release
95 * builds or by hitting a breakpoint in the release builds.
96 *
97 * Usage pattern:
98 * @code
99 try
100 {
101 // ...
102 }
103 catch (LaLalA)
104 {
105 // ...
106 }
107 catch (...)
108 {
109 rc = VirtualBox::handleUnexpectedExceptions(this, RT_SRC_POS);
110 }
111 * @endcode
112 *
113 * @param aThis object where the exception happened
114 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
115 * */
116/* static */
117HRESULT VirtualBoxBase::handleUnexpectedExceptions(VirtualBoxBase *const aThis, RT_SRC_POS_DECL)
118{
119 try
120 {
121 /* re-throw the current exception */
122 throw;
123 }
124 catch (const RTCError &err) // includes all XML exceptions
125 {
126 return setErrorInternal(E_FAIL, aThis->getClassIID(), aThis->getComponentName(),
127 Utf8StrFmt(tr("%s.\n%s[%d] (%s)"),
128 err.what(),
129 pszFile, iLine, pszFunction).c_str(),
130 false /* aWarning */,
131 true /* aLogIt */);
132 }
133 catch (const std::exception &err)
134 {
135 return setErrorInternal(E_FAIL, aThis->getClassIID(), aThis->getComponentName(),
136 Utf8StrFmt(tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
137 err.what(), typeid(err).name(),
138 pszFile, iLine, pszFunction).c_str(),
139 false /* aWarning */,
140 true /* aLogIt */);
141 }
142 catch (...)
143 {
144 return setErrorInternal(E_FAIL, aThis->getClassIID(), aThis->getComponentName(),
145 Utf8StrFmt(tr("Unknown exception\n%s[%d] (%s)"),
146 pszFile, iLine, pszFunction).c_str(),
147 false /* aWarning */,
148 true /* aLogIt */);
149 }
150
151 /* should not get here */
152 AssertFailed();
153 return E_FAIL;
154}
155
156/**
157 * Sets error info for the current thread. This is an internal function that
158 * gets eventually called by all public variants. If @a aWarning is
159 * @c true, then the highest (31) bit in the @a aResultCode value which
160 * indicates the error severity is reset to zero to make sure the receiver will
161 * recognize that the created error info object represents a warning rather
162 * than an error.
163 */
164/* static */
165HRESULT VirtualBoxBase::setErrorInternal(HRESULT aResultCode,
166 const GUID &aIID,
167 const char *pcszComponent,
168 Utf8Str aText,
169 bool aWarning,
170 bool aLogIt)
171{
172 /* whether multi-error mode is turned on */
173 bool preserve = MultiResult::isMultiEnabled();
174
175 if (aLogIt)
176 LogRel(("%s [COM]: aRC=%Rhrc (%#08x) aIID={%RTuuid} aComponent={%s} aText={%s}, preserve=%RTbool\n",
177 aWarning ? "WARNING" : "ERROR",
178 aResultCode,
179 aResultCode,
180 &aIID,
181 pcszComponent,
182 aText.c_str(),
183 aWarning,
184 preserve));
185
186 /* these are mandatory, others -- not */
187 AssertReturn((!aWarning && FAILED(aResultCode)) ||
188 (aWarning && aResultCode != S_OK),
189 E_FAIL);
190
191 /* reset the error severity bit if it's a warning */
192 if (aWarning)
193 aResultCode &= ~0x80000000;
194
195 HRESULT rc = S_OK;
196
197 if (aText.isEmpty())
198 {
199 /* Some default info */
200 switch (aResultCode)
201 {
202 case E_INVALIDARG: aText = "A parameter has an invalid value"; break;
203 case E_POINTER: aText = "A parameter is an invalid pointer"; break;
204 case E_UNEXPECTED: aText = "The result of the operation is unexpected"; break;
205 case E_ACCESSDENIED: aText = "The access to an object is not allowed"; break;
206 case E_OUTOFMEMORY: aText = "The allocation of new memory failed"; break;
207 case E_NOTIMPL: aText = "The requested operation is not implemented"; break;
208 case E_NOINTERFACE: aText = "The requested interface is not implemented"; break;
209 case E_FAIL: aText = "A general error occurred"; break;
210 case E_ABORT: aText = "The operation was canceled"; break;
211 case VBOX_E_OBJECT_NOT_FOUND: aText = "Object corresponding to the supplied arguments does not exist"; break;
212 case VBOX_E_INVALID_VM_STATE: aText = "Current virtual machine state prevents the operation"; break;
213 case VBOX_E_VM_ERROR: aText = "Virtual machine error occurred attempting the operation"; break;
214 case VBOX_E_FILE_ERROR: aText = "File not accessible or erroneous file contents"; break;
215 case VBOX_E_IPRT_ERROR: aText = "Runtime subsystem error"; break;
216 case VBOX_E_PDM_ERROR: aText = "Pluggable Device Manager error"; break;
217 case VBOX_E_INVALID_OBJECT_STATE: aText = "Current object state prohibits operation"; break;
218 case VBOX_E_HOST_ERROR: aText = "Host operating system related error"; break;
219 case VBOX_E_NOT_SUPPORTED: aText = "Requested operation is not supported"; break;
220 case VBOX_E_XML_ERROR: aText = "Invalid XML found"; break;
221 case VBOX_E_INVALID_SESSION_STATE: aText = "Current session state prohibits operation"; break;
222 case VBOX_E_OBJECT_IN_USE: aText = "Object being in use prohibits operation"; break;
223 default: aText = "Unknown error"; break;
224 }
225 }
226
227 do
228 {
229 ComObjPtr<VirtualBoxErrorInfo> info;
230 rc = info.createObject();
231 if (FAILED(rc)) break;
232
233#if !defined(VBOX_WITH_XPCOM)
234
235 ComPtr<IVirtualBoxErrorInfo> curInfo;
236 if (preserve)
237 {
238 /* get the current error info if any */
239 ComPtr<IErrorInfo> err;
240 rc = ::GetErrorInfo(0, err.asOutParam());
241 if (FAILED(rc)) break;
242 rc = err.queryInterfaceTo(curInfo.asOutParam());
243 if (FAILED(rc))
244 {
245 /* create a IVirtualBoxErrorInfo wrapper for the native
246 * IErrorInfo object */
247 ComObjPtr<VirtualBoxErrorInfo> wrapper;
248 rc = wrapper.createObject();
249 if (SUCCEEDED(rc))
250 {
251 rc = wrapper->init(err);
252 if (SUCCEEDED(rc))
253 curInfo = wrapper;
254 }
255 }
256 }
257 /* On failure, curInfo will stay null */
258 Assert(SUCCEEDED(rc) || curInfo.isNull());
259
260 /* set the current error info and preserve the previous one if any */
261 rc = info->init(aResultCode, aIID, pcszComponent, aText, curInfo);
262 if (FAILED(rc)) break;
263
264 ComPtr<IErrorInfo> err;
265 rc = info.queryInterfaceTo(err.asOutParam());
266 if (SUCCEEDED(rc))
267 rc = ::SetErrorInfo(0, err);
268
269#else // !defined(VBOX_WITH_XPCOM)
270
271 nsCOMPtr <nsIExceptionService> es;
272 es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
273 if (NS_SUCCEEDED(rc))
274 {
275 nsCOMPtr <nsIExceptionManager> em;
276 rc = es->GetCurrentExceptionManager(getter_AddRefs(em));
277 if (FAILED(rc)) break;
278
279 ComPtr<IVirtualBoxErrorInfo> curInfo;
280 if (preserve)
281 {
282 /* get the current error info if any */
283 ComPtr<nsIException> ex;
284 rc = em->GetCurrentException(ex.asOutParam());
285 if (FAILED(rc)) break;
286 rc = ex.queryInterfaceTo(curInfo.asOutParam());
287 if (FAILED(rc))
288 {
289 /* create a IVirtualBoxErrorInfo wrapper for the native
290 * nsIException object */
291 ComObjPtr<VirtualBoxErrorInfo> wrapper;
292 rc = wrapper.createObject();
293 if (SUCCEEDED(rc))
294 {
295 rc = wrapper->init(ex);
296 if (SUCCEEDED(rc))
297 curInfo = wrapper;
298 }
299 }
300 }
301 /* On failure, curInfo will stay null */
302 Assert(SUCCEEDED(rc) || curInfo.isNull());
303
304 /* set the current error info and preserve the previous one if any */
305 rc = info->init(aResultCode, aIID, pcszComponent, Bstr(aText), curInfo);
306 if (FAILED(rc)) break;
307
308 ComPtr<nsIException> ex;
309 rc = info.queryInterfaceTo(ex.asOutParam());
310 if (SUCCEEDED(rc))
311 rc = em->SetCurrentException(ex);
312 }
313 else if (rc == NS_ERROR_UNEXPECTED)
314 {
315 /*
316 * It is possible that setError() is being called by the object
317 * after the XPCOM shutdown sequence has been initiated
318 * (for example, when XPCOM releases all instances it internally
319 * references, which can cause object's FinalConstruct() and then
320 * uninit()). In this case, do_GetService() above will return
321 * NS_ERROR_UNEXPECTED and it doesn't actually make sense to
322 * set the exception (nobody will be able to read it).
323 */
324 LogWarningFunc(("Will not set an exception because nsIExceptionService is not available "
325 "(NS_ERROR_UNEXPECTED). XPCOM is being shutdown?\n"));
326 rc = NS_OK;
327 }
328
329#endif // !defined(VBOX_WITH_XPCOM)
330 }
331 while (0);
332
333 AssertComRC(rc);
334
335 return SUCCEEDED(rc) ? aResultCode : rc;
336}
337
338/**
339 * Shortcut instance method to calling the static setErrorInternal with the
340 * class interface ID and component name inserted correctly. This uses the
341 * virtual getClassIID() and getComponentName() methods which are automatically
342 * defined by the VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT macro.
343 * @param aResultCode
344 * @param pcsz
345 * @return
346 */
347HRESULT VirtualBoxBase::setError(HRESULT aResultCode)
348{
349 return setErrorInternal(aResultCode,
350 this->getClassIID(),
351 this->getComponentName(),
352 "",
353 false /* aWarning */,
354 true /* aLogIt */);
355}
356
357/**
358 * Shortcut instance method to calling the static setErrorInternal with the
359 * class interface ID and component name inserted correctly. This uses the
360 * virtual getClassIID() and getComponentName() methods which are automatically
361 * defined by the VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT macro.
362 * @param aResultCode
363 * @return
364 */
365HRESULT VirtualBoxBase::setError(HRESULT aResultCode, const char *pcsz, ...)
366{
367 va_list args;
368 va_start(args, pcsz);
369 HRESULT rc = setErrorInternal(aResultCode,
370 this->getClassIID(),
371 this->getComponentName(),
372 Utf8Str(pcsz, args),
373 false /* aWarning */,
374 true /* aLogIt */);
375 va_end(args);
376 return rc;
377}
378
379/**
380 * Shortcut instance method to calling the static setErrorInternal with the
381 * class interface ID and component name inserted correctly. This uses the
382 * virtual getClassIID() and getComponentName() methods which are automatically
383 * defined by the VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT macro.
384 * @param ei
385 * @return
386 */
387HRESULT VirtualBoxBase::setError(const com::ErrorInfo &ei)
388{
389 /* whether multi-error mode is turned on */
390 bool preserve = MultiResult::isMultiEnabled();
391
392 HRESULT rc = S_OK;
393
394 do
395 {
396 ComObjPtr<VirtualBoxErrorInfo> info;
397 rc = info.createObject();
398 if (FAILED(rc)) break;
399
400#if !defined(VBOX_WITH_XPCOM)
401
402 ComPtr<IVirtualBoxErrorInfo> curInfo;
403 if (preserve)
404 {
405 /* get the current error info if any */
406 ComPtr<IErrorInfo> err;
407 rc = ::GetErrorInfo(0, err.asOutParam());
408 if (FAILED(rc)) break;
409 rc = err.queryInterfaceTo(curInfo.asOutParam());
410 if (FAILED(rc))
411 {
412 /* create a IVirtualBoxErrorInfo wrapper for the native
413 * IErrorInfo object */
414 ComObjPtr<VirtualBoxErrorInfo> wrapper;
415 rc = wrapper.createObject();
416 if (SUCCEEDED(rc))
417 {
418 rc = wrapper->init(err);
419 if (SUCCEEDED(rc))
420 curInfo = wrapper;
421 }
422 }
423 }
424 /* On failure, curInfo will stay null */
425 Assert(SUCCEEDED(rc) || curInfo.isNull());
426
427 /* set the current error info and preserve the previous one if any */
428 rc = info->init(ei, curInfo);
429 if (FAILED(rc)) break;
430
431 ComPtr<IErrorInfo> err;
432 rc = info.queryInterfaceTo(err.asOutParam());
433 if (SUCCEEDED(rc))
434 rc = ::SetErrorInfo(0, err);
435
436#else // !defined(VBOX_WITH_XPCOM)
437
438 nsCOMPtr <nsIExceptionService> es;
439 es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
440 if (NS_SUCCEEDED(rc))
441 {
442 nsCOMPtr <nsIExceptionManager> em;
443 rc = es->GetCurrentExceptionManager(getter_AddRefs(em));
444 if (FAILED(rc)) break;
445
446 ComPtr<IVirtualBoxErrorInfo> curInfo;
447 if (preserve)
448 {
449 /* get the current error info if any */
450 ComPtr<nsIException> ex;
451 rc = em->GetCurrentException(ex.asOutParam());
452 if (FAILED(rc)) break;
453 rc = ex.queryInterfaceTo(curInfo.asOutParam());
454 if (FAILED(rc))
455 {
456 /* create a IVirtualBoxErrorInfo wrapper for the native
457 * nsIException object */
458 ComObjPtr<VirtualBoxErrorInfo> wrapper;
459 rc = wrapper.createObject();
460 if (SUCCEEDED(rc))
461 {
462 rc = wrapper->init(ex);
463 if (SUCCEEDED(rc))
464 curInfo = wrapper;
465 }
466 }
467 }
468 /* On failure, curInfo will stay null */
469 Assert(SUCCEEDED(rc) || curInfo.isNull());
470
471 /* set the current error info and preserve the previous one if any */
472 rc = info->init(ei, curInfo);
473 if (FAILED(rc)) break;
474
475 ComPtr<nsIException> ex;
476 rc = info.queryInterfaceTo(ex.asOutParam());
477 if (SUCCEEDED(rc))
478 rc = em->SetCurrentException(ex);
479 }
480 else if (rc == NS_ERROR_UNEXPECTED)
481 {
482 /*
483 * It is possible that setError() is being called by the object
484 * after the XPCOM shutdown sequence has been initiated
485 * (for example, when XPCOM releases all instances it internally
486 * references, which can cause object's FinalConstruct() and then
487 * uninit()). In this case, do_GetService() above will return
488 * NS_ERROR_UNEXPECTED and it doesn't actually make sense to
489 * set the exception (nobody will be able to read it).
490 */
491 LogWarningFunc(("Will not set an exception because nsIExceptionService is not available "
492 "(NS_ERROR_UNEXPECTED). XPCOM is being shutdown?\n"));
493 rc = NS_OK;
494 }
495
496#endif // !defined(VBOX_WITH_XPCOM)
497 }
498 while (0);
499
500 AssertComRC(rc);
501
502 return SUCCEEDED(rc) ? ei.getResultCode() : rc;
503}
504
505/**
506 * Like setError(), but sets the "warning" bit in the call to setErrorInternal().
507 * @param aResultCode
508 * @param pcsz
509 * @return
510 */
511HRESULT VirtualBoxBase::setWarning(HRESULT aResultCode, const char *pcsz, ...)
512{
513 va_list args;
514 va_start(args, pcsz);
515 HRESULT rc = setErrorInternal(aResultCode,
516 this->getClassIID(),
517 this->getComponentName(),
518 Utf8Str(pcsz, args),
519 true /* aWarning */,
520 true /* aLogIt */);
521 va_end(args);
522 return rc;
523}
524
525/**
526 * Like setError(), but disables the "log" flag in the call to setErrorInternal().
527 * @param aResultCode
528 * @param pcsz
529 * @return
530 */
531HRESULT VirtualBoxBase::setErrorNoLog(HRESULT aResultCode, const char *pcsz, ...)
532{
533 va_list args;
534 va_start(args, pcsz);
535 HRESULT rc = setErrorInternal(aResultCode,
536 this->getClassIID(),
537 this->getComponentName(),
538 Utf8Str(pcsz, args),
539 false /* aWarning */,
540 false /* aLogIt */);
541 va_end(args);
542 return rc;
543}
544
545/**
546 * Clear the current error information.
547 */
548/*static*/
549void VirtualBoxBase::clearError(void)
550{
551#if !defined(VBOX_WITH_XPCOM)
552 ::SetErrorInfo(0, NULL);
553#else
554 HRESULT rc = S_OK;
555 nsCOMPtr <nsIExceptionService> es;
556 es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
557 if (NS_SUCCEEDED(rc))
558 {
559 nsCOMPtr <nsIExceptionManager> em;
560 rc = es->GetCurrentExceptionManager(getter_AddRefs(em));
561 if (SUCCEEDED(rc))
562 em->SetCurrentException(NULL);
563 }
564#endif
565}
566
567
568////////////////////////////////////////////////////////////////////////////////
569//
570// MultiResult methods
571//
572////////////////////////////////////////////////////////////////////////////////
573
574RTTLS MultiResult::sCounter = NIL_RTTLS;
575
576/*static*/
577void MultiResult::incCounter()
578{
579 if (sCounter == NIL_RTTLS)
580 {
581 sCounter = RTTlsAlloc();
582 AssertReturnVoid(sCounter != NIL_RTTLS);
583 }
584
585 uintptr_t counter = (uintptr_t)RTTlsGet(sCounter);
586 ++counter;
587 RTTlsSet(sCounter, (void*)counter);
588}
589
590/*static*/
591void MultiResult::decCounter()
592{
593 uintptr_t counter = (uintptr_t)RTTlsGet(sCounter);
594 AssertReturnVoid(counter != 0);
595 --counter;
596 RTTlsSet(sCounter, (void*)counter);
597}
598
599/*static*/
600bool MultiResult::isMultiEnabled()
601{
602 if (sCounter == NIL_RTTLS)
603 return false;
604
605 return ((uintptr_t)RTTlsGet(MultiResult::sCounter)) > 0;
606}
607
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