VirtualBox

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

Last change on this file since 40685 was 40257, checked in by vboxsync, 13 years ago

Main/Medium: rework locking scheme to solve lock order violations and long GUI start up time caused by too much locking
Main/all: Remove the enter and leave methods from write locks, they cause hard to find locking problems. Better solve them explicitly.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.4 KB
Line 
1/* $Id: VirtualBoxBase.cpp 40257 2012-02-27 09:25:12Z vboxsync $ */
2
3/** @file
4 *
5 * VirtualBox COM base classes implementation
6 */
7
8/*
9 * Copyright (C) 2006-2012 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
23#if !defined (VBOX_WITH_XPCOM)
24#include <windows.h>
25#include <dbghelp.h>
26#else /* !defined (VBOX_WITH_XPCOM) */
27/// @todo remove when VirtualBoxErrorInfo goes away from here
28#include <nsIServiceManager.h>
29#include <nsIExceptionService.h>
30#endif /* !defined (VBOX_WITH_XPCOM) */
31
32#include "VirtualBoxBase.h"
33#include "AutoCaller.h"
34#include "VirtualBoxErrorInfoImpl.h"
35#include "Logging.h"
36
37#include "VBox/com/ErrorInfo.h"
38#include "VBox/com/MultiResult.h"
39
40////////////////////////////////////////////////////////////////////////////////
41//
42// VirtualBoxBase
43//
44////////////////////////////////////////////////////////////////////////////////
45
46VirtualBoxBase::VirtualBoxBase()
47 : mStateLock(LOCKCLASS_OBJECTSTATE)
48{
49 mState = NotReady;
50 mStateChangeThread = NIL_RTTHREAD;
51 mCallers = 0;
52 mZeroCallersSem = NIL_RTSEMEVENT;
53 mInitUninitSem = NIL_RTSEMEVENTMULTI;
54 mInitUninitWaiters = 0;
55 mObjectLock = NULL;
56}
57
58VirtualBoxBase::~VirtualBoxBase()
59{
60 if (mObjectLock)
61 delete mObjectLock;
62 Assert(mInitUninitWaiters == 0);
63 Assert(mInitUninitSem == NIL_RTSEMEVENTMULTI);
64 if (mZeroCallersSem != NIL_RTSEMEVENT)
65 RTSemEventDestroy (mZeroCallersSem);
66 mCallers = 0;
67 mStateChangeThread = NIL_RTTHREAD;
68 mState = NotReady;
69}
70
71/**
72 * This virtual method returns an RWLockHandle that can be used to
73 * protect instance data. This RWLockHandle is generally referred to
74 * as the "object lock"; its locking class (for lock order validation)
75 * must be returned by another virtual method, getLockingClass(), which
76 * by default returns LOCKCLASS_OTHEROBJECT but is overridden by several
77 * subclasses such as VirtualBox, Host, Machine and others.
78 *
79 * On the first call this method lazily creates the RWLockHandle.
80 *
81 * @return
82 */
83/* virtual */
84RWLockHandle *VirtualBoxBase::lockHandle() const
85{
86 /* lazy initialization */
87 if (RT_UNLIKELY(!mObjectLock))
88 {
89 AssertCompile (sizeof (RWLockHandle *) == sizeof (void *));
90
91 // getLockingClass() is overridden by many subclasses to return
92 // one of the locking classes listed at the top of AutoLock.h
93 RWLockHandle *objLock = new RWLockHandle(getLockingClass());
94 if (!ASMAtomicCmpXchgPtr(&mObjectLock, objLock, NULL))
95 {
96 delete objLock;
97 objLock = ASMAtomicReadPtrT(&mObjectLock, RWLockHandle *);
98 }
99 return objLock;
100 }
101 return mObjectLock;
102}
103
104/**
105 * Increments the number of calls to this object by one.
106 *
107 * After this method succeeds, it is guaranteed that the object will remain
108 * in the Ready (or in the Limited) state at least until #releaseCaller() is
109 * called.
110 *
111 * This method is intended to mark the beginning of sections of code within
112 * methods of COM objects that depend on the readiness (Ready) state. The
113 * Ready state is a primary "ready to serve" state. Usually all code that
114 * works with component's data depends on it. On practice, this means that
115 * almost every public method, setter or getter of the object should add
116 * itself as an object's caller at the very beginning, to protect from an
117 * unexpected uninitialization that may happen on a different thread.
118 *
119 * Besides the Ready state denoting that the object is fully functional,
120 * there is a special Limited state. The Limited state means that the object
121 * is still functional, but its functionality is limited to some degree, so
122 * not all operations are possible. The @a aLimited argument to this method
123 * determines whether the caller represents this limited functionality or
124 * not.
125 *
126 * This method succeeds (and increments the number of callers) only if the
127 * current object's state is Ready. Otherwise, it will return E_ACCESSDENIED
128 * to indicate that the object is not operational. There are two exceptions
129 * from this rule:
130 * <ol>
131 * <li>If the @a aLimited argument is |true|, then this method will also
132 * succeed if the object's state is Limited (or Ready, of course).
133 * </li>
134 * <li>If this method is called from the same thread that placed
135 * the object to InInit or InUninit state (i.e. either from within the
136 * AutoInitSpan or AutoUninitSpan scope), it will succeed as well (but
137 * will not increase the number of callers).
138 * </li>
139 * </ol>
140 *
141 * Normally, calling addCaller() never blocks. However, if this method is
142 * called by a thread created from within the AutoInitSpan scope and this
143 * scope is still active (i.e. the object state is InInit), it will block
144 * until the AutoInitSpan destructor signals that it has finished
145 * initialization.
146 *
147 * When this method returns a failure, the caller must not use the object
148 * and should return the failed result code to its own caller.
149 *
150 * @param aState Where to store the current object's state (can be
151 * used in overridden methods to determine the cause of
152 * the failure).
153 * @param aLimited |true| to add a limited caller.
154 *
155 * @return S_OK on success or E_ACCESSDENIED on failure.
156 *
157 * @note It is preferable to use the #addLimitedCaller() rather than
158 * calling this method with @a aLimited = |true|, for better
159 * self-descriptiveness.
160 *
161 * @sa #addLimitedCaller()
162 * @sa #releaseCaller()
163 */
164HRESULT VirtualBoxBase::addCaller(State *aState /* = NULL */,
165 bool aLimited /* = false */)
166{
167 AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
168
169 HRESULT rc = E_ACCESSDENIED;
170
171 if (mState == Ready || (aLimited && mState == Limited))
172 {
173 /* if Ready or allows Limited, increase the number of callers */
174 ++ mCallers;
175 rc = S_OK;
176 }
177 else
178 if (mState == InInit || mState == InUninit)
179 {
180 if (mStateChangeThread == RTThreadSelf())
181 {
182 /* Called from the same thread that is doing AutoInitSpan or
183 * AutoUninitSpan, just succeed */
184 rc = S_OK;
185 }
186 else if (mState == InInit)
187 {
188 /* addCaller() is called by a "child" thread while the "parent"
189 * thread is still doing AutoInitSpan/AutoReinitSpan, so wait for
190 * the state to become either Ready/Limited or InitFailed (in
191 * case of init failure).
192 *
193 * Note that we increase the number of callers anyway -- to
194 * prevent AutoUninitSpan from early completion if we are
195 * still not scheduled to pick up the posted semaphore when
196 * uninit() is called.
197 */
198 ++ mCallers;
199
200 /* lazy semaphore creation */
201 if (mInitUninitSem == NIL_RTSEMEVENTMULTI)
202 {
203 RTSemEventMultiCreate (&mInitUninitSem);
204 Assert(mInitUninitWaiters == 0);
205 }
206
207 ++ mInitUninitWaiters;
208
209 LogFlowThisFunc(("Waiting for AutoInitSpan/AutoReinitSpan to finish...\n"));
210
211 stateLock.release();
212 RTSemEventMultiWait (mInitUninitSem, RT_INDEFINITE_WAIT);
213 stateLock.acquire();
214
215 if (-- mInitUninitWaiters == 0)
216 {
217 /* destroy the semaphore since no more necessary */
218 RTSemEventMultiDestroy (mInitUninitSem);
219 mInitUninitSem = NIL_RTSEMEVENTMULTI;
220 }
221
222 if (mState == Ready || (aLimited && mState == Limited))
223 rc = S_OK;
224 else
225 {
226 Assert(mCallers != 0);
227 -- mCallers;
228 if (mCallers == 0 && mState == InUninit)
229 {
230 /* inform AutoUninitSpan ctor there are no more callers */
231 RTSemEventSignal(mZeroCallersSem);
232 }
233 }
234 }
235 }
236
237 if (aState)
238 *aState = mState;
239
240 if (FAILED(rc))
241 {
242 if (mState == VirtualBoxBase::Limited)
243 rc = setError(rc, "The object functionality is limited");
244 else
245 rc = setError(rc, "The object is not ready");
246 }
247
248 return rc;
249}
250
251/**
252 * Decreases the number of calls to this object by one.
253 *
254 * Must be called after every #addCaller() or #addLimitedCaller() when
255 * protecting the object from uninitialization is no more necessary.
256 */
257void VirtualBoxBase::releaseCaller()
258{
259 AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
260
261 if (mState == Ready || mState == Limited)
262 {
263 /* if Ready or Limited, decrease the number of callers */
264 AssertMsgReturn(mCallers != 0, ("mCallers is ZERO!"), (void) 0);
265 --mCallers;
266
267 return;
268 }
269
270 if (mState == InInit || mState == InUninit)
271 {
272 if (mStateChangeThread == RTThreadSelf())
273 {
274 /* Called from the same thread that is doing AutoInitSpan or
275 * AutoUninitSpan: just succeed */
276 return;
277 }
278
279 if (mState == InUninit)
280 {
281 /* the caller is being released after AutoUninitSpan has begun */
282 AssertMsgReturn(mCallers != 0, ("mCallers is ZERO!"), (void) 0);
283 --mCallers;
284
285 if (mCallers == 0)
286 /* inform the Auto*UninitSpan ctor there are no more callers */
287 RTSemEventSignal(mZeroCallersSem);
288
289 return;
290 }
291 }
292
293 AssertMsgFailed (("mState = %d!", mState));
294}
295
296/**
297 * Sets error info for the current thread. This is an internal function that
298 * gets eventually called by all public variants. If @a aWarning is
299 * @c true, then the highest (31) bit in the @a aResultCode value which
300 * indicates the error severity is reset to zero to make sure the receiver will
301 * recognize that the created error info object represents a warning rather
302 * than an error.
303 */
304/* static */
305HRESULT VirtualBoxBase::setErrorInternal(HRESULT aResultCode,
306 const GUID &aIID,
307 const char *pcszComponent,
308 Utf8Str aText,
309 bool aWarning,
310 bool aLogIt)
311{
312 /* whether multi-error mode is turned on */
313 bool preserve = MultiResult::isMultiEnabled();
314
315 if (aLogIt)
316 LogRel(("%s [COM]: aRC=%Rhrc (%#08x) aIID={%RTuuid} aComponent={%s} aText={%s}, preserve=%RTbool\n",
317 aWarning ? "WARNING" : "ERROR",
318 aResultCode,
319 aResultCode,
320 &aIID,
321 pcszComponent,
322 aText.c_str(),
323 aWarning,
324 preserve));
325
326 /* these are mandatory, others -- not */
327 AssertReturn((!aWarning && FAILED(aResultCode)) ||
328 (aWarning && aResultCode != S_OK),
329 E_FAIL);
330
331 /* reset the error severity bit if it's a warning */
332 if (aWarning)
333 aResultCode &= ~0x80000000;
334
335 HRESULT rc = S_OK;
336
337 if (aText.isEmpty())
338 {
339 /* Some default info */
340 switch (aResultCode)
341 {
342 case E_INVALIDARG: aText = "A parameter has an invalid value"; break;
343 case E_POINTER: aText = "A parameter is an invalid pointer"; break;
344 case E_UNEXPECTED: aText = "The result of the operation is unexpected"; break;
345 case E_ACCESSDENIED: aText = "The access to an object is not allowed"; break;
346 case E_OUTOFMEMORY: aText = "The allocation of new memory failed"; break;
347 case E_NOTIMPL: aText = "The requested operation is not implemented"; break;
348 case E_NOINTERFACE: aText = "The requested interface is not implemented"; break;
349 case E_FAIL: aText = "A general error occurred"; break;
350 case E_ABORT: aText = "The operation was canceled"; break;
351 case VBOX_E_OBJECT_NOT_FOUND: aText = "Object corresponding to the supplied arguments does not exist"; break;
352 case VBOX_E_INVALID_VM_STATE: aText = "Current virtual machine state prevents the operation"; break;
353 case VBOX_E_VM_ERROR: aText = "Virtual machine error occurred attempting the operation"; break;
354 case VBOX_E_FILE_ERROR: aText = "File not accessible or erroneous file contents"; break;
355 case VBOX_E_IPRT_ERROR: aText = "Runtime subsystem error"; break;
356 case VBOX_E_PDM_ERROR: aText = "Pluggable Device Manager error"; break;
357 case VBOX_E_INVALID_OBJECT_STATE: aText = "Current object state prohibits operation"; break;
358 case VBOX_E_HOST_ERROR: aText = "Host operating system related error"; break;
359 case VBOX_E_NOT_SUPPORTED: aText = "Requested operation is not supported"; break;
360 case VBOX_E_XML_ERROR: aText = "Invalid XML found"; break;
361 case VBOX_E_INVALID_SESSION_STATE: aText = "Current session state prohibits operation"; break;
362 case VBOX_E_OBJECT_IN_USE: aText = "Object being in use prohibits operation"; break;
363 default: aText = "Unknown error"; break;
364 }
365 }
366
367 do
368 {
369 ComObjPtr<VirtualBoxErrorInfo> info;
370 rc = info.createObject();
371 if (FAILED(rc)) break;
372
373#if !defined (VBOX_WITH_XPCOM)
374
375 ComPtr<IVirtualBoxErrorInfo> curInfo;
376 if (preserve)
377 {
378 /* get the current error info if any */
379 ComPtr<IErrorInfo> err;
380 rc = ::GetErrorInfo (0, err.asOutParam());
381 if (FAILED(rc)) break;
382 rc = err.queryInterfaceTo(curInfo.asOutParam());
383 if (FAILED(rc))
384 {
385 /* create a IVirtualBoxErrorInfo wrapper for the native
386 * IErrorInfo object */
387 ComObjPtr<VirtualBoxErrorInfo> wrapper;
388 rc = wrapper.createObject();
389 if (SUCCEEDED(rc))
390 {
391 rc = wrapper->init (err);
392 if (SUCCEEDED(rc))
393 curInfo = wrapper;
394 }
395 }
396 }
397 /* On failure, curInfo will stay null */
398 Assert(SUCCEEDED(rc) || curInfo.isNull());
399
400 /* set the current error info and preserve the previous one if any */
401 rc = info->init(aResultCode, aIID, pcszComponent, aText, curInfo);
402 if (FAILED(rc)) break;
403
404 ComPtr<IErrorInfo> err;
405 rc = info.queryInterfaceTo(err.asOutParam());
406 if (SUCCEEDED(rc))
407 rc = ::SetErrorInfo (0, err);
408
409#else // !defined (VBOX_WITH_XPCOM)
410
411 nsCOMPtr <nsIExceptionService> es;
412 es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
413 if (NS_SUCCEEDED(rc))
414 {
415 nsCOMPtr <nsIExceptionManager> em;
416 rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
417 if (FAILED(rc)) break;
418
419 ComPtr<IVirtualBoxErrorInfo> curInfo;
420 if (preserve)
421 {
422 /* get the current error info if any */
423 ComPtr<nsIException> ex;
424 rc = em->GetCurrentException (ex.asOutParam());
425 if (FAILED(rc)) break;
426 rc = ex.queryInterfaceTo(curInfo.asOutParam());
427 if (FAILED(rc))
428 {
429 /* create a IVirtualBoxErrorInfo wrapper for the native
430 * nsIException object */
431 ComObjPtr<VirtualBoxErrorInfo> wrapper;
432 rc = wrapper.createObject();
433 if (SUCCEEDED(rc))
434 {
435 rc = wrapper->init (ex);
436 if (SUCCEEDED(rc))
437 curInfo = wrapper;
438 }
439 }
440 }
441 /* On failure, curInfo will stay null */
442 Assert(SUCCEEDED(rc) || curInfo.isNull());
443
444 /* set the current error info and preserve the previous one if any */
445 rc = info->init(aResultCode, aIID, pcszComponent, Bstr(aText), curInfo);
446 if (FAILED(rc)) break;
447
448 ComPtr<nsIException> ex;
449 rc = info.queryInterfaceTo(ex.asOutParam());
450 if (SUCCEEDED(rc))
451 rc = em->SetCurrentException (ex);
452 }
453 else if (rc == NS_ERROR_UNEXPECTED)
454 {
455 /*
456 * It is possible that setError() is being called by the object
457 * after the XPCOM shutdown sequence has been initiated
458 * (for example, when XPCOM releases all instances it internally
459 * references, which can cause object's FinalConstruct() and then
460 * uninit()). In this case, do_GetService() above will return
461 * NS_ERROR_UNEXPECTED and it doesn't actually make sense to
462 * set the exception (nobody will be able to read it).
463 */
464 LogWarningFunc(("Will not set an exception because nsIExceptionService is not available "
465 "(NS_ERROR_UNEXPECTED). XPCOM is being shutdown?\n"));
466 rc = NS_OK;
467 }
468
469#endif // !defined (VBOX_WITH_XPCOM)
470 }
471 while (0);
472
473 AssertComRC (rc);
474
475 return SUCCEEDED(rc) ? aResultCode : rc;
476}
477
478/**
479 * Shortcut instance method to calling the static setErrorInternal with the
480 * class interface ID and component name inserted correctly. This uses the
481 * virtual getClassIID() and getComponentName() methods which are automatically
482 * defined by the VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT macro.
483 * @param aResultCode
484 * @param pcsz
485 * @return
486 */
487HRESULT VirtualBoxBase::setError(HRESULT aResultCode)
488{
489 return setErrorInternal(aResultCode,
490 this->getClassIID(),
491 this->getComponentName(),
492 "",
493 false /* aWarning */,
494 true /* aLogIt */);
495}
496
497/**
498 * Shortcut instance method to calling the static setErrorInternal with the
499 * class interface ID and component name inserted correctly. This uses the
500 * virtual getClassIID() and getComponentName() methods which are automatically
501 * defined by the VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT macro.
502 * @param aResultCode
503 * @return
504 */
505HRESULT VirtualBoxBase::setError(HRESULT aResultCode, const char *pcsz, ...)
506{
507 va_list args;
508 va_start(args, pcsz);
509 HRESULT rc = setErrorInternal(aResultCode,
510 this->getClassIID(),
511 this->getComponentName(),
512 Utf8Str(pcsz, args),
513 false /* aWarning */,
514 true /* aLogIt */);
515 va_end(args);
516 return rc;
517}
518
519/**
520 * Shortcut instance method to calling the static setErrorInternal with the
521 * class interface ID and component name inserted correctly. This uses the
522 * virtual getClassIID() and getComponentName() methods which are automatically
523 * defined by the VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT macro.
524 * @param ei
525 * @return
526 */
527HRESULT VirtualBoxBase::setError(const com::ErrorInfo &ei)
528{
529 /* whether multi-error mode is turned on */
530 bool preserve = MultiResult::isMultiEnabled();
531
532 HRESULT rc = S_OK;
533
534 do
535 {
536 ComObjPtr<VirtualBoxErrorInfo> info;
537 rc = info.createObject();
538 if (FAILED(rc)) break;
539
540#if !defined (VBOX_WITH_XPCOM)
541
542 ComPtr<IVirtualBoxErrorInfo> curInfo;
543 if (preserve)
544 {
545 /* get the current error info if any */
546 ComPtr<IErrorInfo> err;
547 rc = ::GetErrorInfo (0, err.asOutParam());
548 if (FAILED(rc)) break;
549 rc = err.queryInterfaceTo(curInfo.asOutParam());
550 if (FAILED(rc))
551 {
552 /* create a IVirtualBoxErrorInfo wrapper for the native
553 * IErrorInfo object */
554 ComObjPtr<VirtualBoxErrorInfo> wrapper;
555 rc = wrapper.createObject();
556 if (SUCCEEDED(rc))
557 {
558 rc = wrapper->init (err);
559 if (SUCCEEDED(rc))
560 curInfo = wrapper;
561 }
562 }
563 }
564 /* On failure, curInfo will stay null */
565 Assert(SUCCEEDED(rc) || curInfo.isNull());
566
567 /* set the current error info and preserve the previous one if any */
568 rc = info->init(ei, curInfo);
569 if (FAILED(rc)) break;
570
571 ComPtr<IErrorInfo> err;
572 rc = info.queryInterfaceTo(err.asOutParam());
573 if (SUCCEEDED(rc))
574 rc = ::SetErrorInfo (0, err);
575
576#else // !defined (VBOX_WITH_XPCOM)
577
578 nsCOMPtr <nsIExceptionService> es;
579 es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
580 if (NS_SUCCEEDED(rc))
581 {
582 nsCOMPtr <nsIExceptionManager> em;
583 rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
584 if (FAILED(rc)) break;
585
586 ComPtr<IVirtualBoxErrorInfo> curInfo;
587 if (preserve)
588 {
589 /* get the current error info if any */
590 ComPtr<nsIException> ex;
591 rc = em->GetCurrentException (ex.asOutParam());
592 if (FAILED(rc)) break;
593 rc = ex.queryInterfaceTo(curInfo.asOutParam());
594 if (FAILED(rc))
595 {
596 /* create a IVirtualBoxErrorInfo wrapper for the native
597 * nsIException object */
598 ComObjPtr<VirtualBoxErrorInfo> wrapper;
599 rc = wrapper.createObject();
600 if (SUCCEEDED(rc))
601 {
602 rc = wrapper->init (ex);
603 if (SUCCEEDED(rc))
604 curInfo = wrapper;
605 }
606 }
607 }
608 /* On failure, curInfo will stay null */
609 Assert(SUCCEEDED(rc) || curInfo.isNull());
610
611 /* set the current error info and preserve the previous one if any */
612 rc = info->init(ei, curInfo);
613 if (FAILED(rc)) break;
614
615 ComPtr<nsIException> ex;
616 rc = info.queryInterfaceTo(ex.asOutParam());
617 if (SUCCEEDED(rc))
618 rc = em->SetCurrentException (ex);
619 }
620 else if (rc == NS_ERROR_UNEXPECTED)
621 {
622 /*
623 * It is possible that setError() is being called by the object
624 * after the XPCOM shutdown sequence has been initiated
625 * (for example, when XPCOM releases all instances it internally
626 * references, which can cause object's FinalConstruct() and then
627 * uninit()). In this case, do_GetService() above will return
628 * NS_ERROR_UNEXPECTED and it doesn't actually make sense to
629 * set the exception (nobody will be able to read it).
630 */
631 LogWarningFunc(("Will not set an exception because nsIExceptionService is not available "
632 "(NS_ERROR_UNEXPECTED). XPCOM is being shutdown?\n"));
633 rc = NS_OK;
634 }
635
636#endif // !defined (VBOX_WITH_XPCOM)
637 }
638 while (0);
639
640 AssertComRC (rc);
641
642 return SUCCEEDED(rc) ? ei.getResultCode() : rc;
643}
644
645/**
646 * Like setError(), but sets the "warning" bit in the call to setErrorInternal().
647 * @param aResultCode
648 * @param pcsz
649 * @return
650 */
651HRESULT VirtualBoxBase::setWarning(HRESULT aResultCode, const char *pcsz, ...)
652{
653 va_list args;
654 va_start(args, pcsz);
655 HRESULT rc = setErrorInternal(aResultCode,
656 this->getClassIID(),
657 this->getComponentName(),
658 Utf8Str(pcsz, args),
659 true /* aWarning */,
660 true /* aLogIt */);
661 va_end(args);
662 return rc;
663}
664
665/**
666 * Like setError(), but disables the "log" flag in the call to setErrorInternal().
667 * @param aResultCode
668 * @param pcsz
669 * @return
670 */
671HRESULT VirtualBoxBase::setErrorNoLog(HRESULT aResultCode, const char *pcsz, ...)
672{
673 va_list args;
674 va_start(args, pcsz);
675 HRESULT rc = setErrorInternal(aResultCode,
676 this->getClassIID(),
677 this->getComponentName(),
678 Utf8Str(pcsz, args),
679 false /* aWarning */,
680 false /* aLogIt */);
681 va_end(args);
682 return rc;
683}
684
685/**
686 * Clear the current error information.
687 */
688/*static*/
689void VirtualBoxBase::clearError(void)
690{
691#if !defined(VBOX_WITH_XPCOM)
692 ::SetErrorInfo (0, NULL);
693#else
694 HRESULT rc = S_OK;
695 nsCOMPtr <nsIExceptionService> es;
696 es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
697 if (NS_SUCCEEDED(rc))
698 {
699 nsCOMPtr <nsIExceptionManager> em;
700 rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
701 if (SUCCEEDED(rc))
702 em->SetCurrentException(NULL);
703 }
704#endif
705}
706
707
708////////////////////////////////////////////////////////////////////////////////
709//
710// AutoInitSpan methods
711//
712////////////////////////////////////////////////////////////////////////////////
713
714/**
715 * Creates a smart initialization span object that places the object to
716 * InInit state.
717 *
718 * Please see the AutoInitSpan class description for more info.
719 *
720 * @param aObj |this| pointer of the managed VirtualBoxBase object whose
721 * init() method is being called.
722 * @param aResult Default initialization result.
723 */
724AutoInitSpan::AutoInitSpan(VirtualBoxBase *aObj,
725 Result aResult /* = Failed */)
726 : mObj(aObj),
727 mResult(aResult),
728 mOk(false)
729{
730 Assert(aObj);
731
732 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
733
734 mOk = mObj->mState == VirtualBoxBase::NotReady;
735 AssertReturnVoid (mOk);
736
737 mObj->setState(VirtualBoxBase::InInit);
738}
739
740/**
741 * Places the managed VirtualBoxBase object to Ready/Limited state if the
742 * initialization succeeded or partly succeeded, or places it to InitFailed
743 * state and calls the object's uninit() method.
744 *
745 * Please see the AutoInitSpan class description for more info.
746 */
747AutoInitSpan::~AutoInitSpan()
748{
749 /* if the state was other than NotReady, do nothing */
750 if (!mOk)
751 return;
752
753 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
754
755 Assert(mObj->mState == VirtualBoxBase::InInit);
756
757 if (mObj->mCallers > 0)
758 {
759 Assert(mObj->mInitUninitWaiters > 0);
760
761 /* We have some pending addCaller() calls on other threads (created
762 * during InInit), signal that InInit is finished and they may go on. */
763 RTSemEventMultiSignal(mObj->mInitUninitSem);
764 }
765
766 if (mResult == Succeeded)
767 {
768 mObj->setState(VirtualBoxBase::Ready);
769 }
770 else
771 if (mResult == Limited)
772 {
773 mObj->setState(VirtualBoxBase::Limited);
774 }
775 else
776 {
777 mObj->setState(VirtualBoxBase::InitFailed);
778 /* release the lock to prevent nesting when uninit() is called */
779 stateLock.acquire();
780 /* call uninit() to let the object uninit itself after failed init() */
781 mObj->uninit();
782 /* Note: the object may no longer exist here (for example, it can call
783 * the destructor in uninit()) */
784 }
785}
786
787// AutoReinitSpan methods
788////////////////////////////////////////////////////////////////////////////////
789
790/**
791 * Creates a smart re-initialization span object and places the object to
792 * InInit state.
793 *
794 * Please see the AutoInitSpan class description for more info.
795 *
796 * @param aObj |this| pointer of the managed VirtualBoxBase object whose
797 * re-initialization method is being called.
798 */
799AutoReinitSpan::AutoReinitSpan(VirtualBoxBase *aObj)
800 : mObj(aObj),
801 mSucceeded(false),
802 mOk(false)
803{
804 Assert(aObj);
805
806 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
807
808 mOk = mObj->mState == VirtualBoxBase::Limited;
809 AssertReturnVoid (mOk);
810
811 mObj->setState(VirtualBoxBase::InInit);
812}
813
814/**
815 * Places the managed VirtualBoxBase object to Ready state if the
816 * re-initialization succeeded (i.e. #setSucceeded() has been called) or back to
817 * Limited state otherwise.
818 *
819 * Please see the AutoInitSpan class description for more info.
820 */
821AutoReinitSpan::~AutoReinitSpan()
822{
823 /* if the state was other than Limited, do nothing */
824 if (!mOk)
825 return;
826
827 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
828
829 Assert(mObj->mState == VirtualBoxBase::InInit);
830
831 if (mObj->mCallers > 0 && mObj->mInitUninitWaiters > 0)
832 {
833 /* We have some pending addCaller() calls on other threads (created
834 * during InInit), signal that InInit is finished and they may go on. */
835 RTSemEventMultiSignal(mObj->mInitUninitSem);
836 }
837
838 if (mSucceeded)
839 {
840 mObj->setState(VirtualBoxBase::Ready);
841 }
842 else
843 {
844 mObj->setState(VirtualBoxBase::Limited);
845 }
846}
847
848// AutoUninitSpan methods
849////////////////////////////////////////////////////////////////////////////////
850
851/**
852 * Creates a smart uninitialization span object and places this object to
853 * InUninit state.
854 *
855 * Please see the AutoInitSpan class description for more info.
856 *
857 * @note This method blocks the current thread execution until the number of
858 * callers of the managed VirtualBoxBase object drops to zero!
859 *
860 * @param aObj |this| pointer of the VirtualBoxBase object whose uninit()
861 * method is being called.
862 */
863AutoUninitSpan::AutoUninitSpan(VirtualBoxBase *aObj)
864 : mObj(aObj),
865 mInitFailed(false),
866 mUninitDone(false)
867{
868 Assert(aObj);
869
870 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
871
872 Assert(mObj->mState != VirtualBoxBase::InInit);
873
874 /* Set mUninitDone to |true| if this object is already uninitialized
875 * (NotReady) or if another AutoUninitSpan is currently active on some
876 * other thread (InUninit). */
877 mUninitDone = mObj->mState == VirtualBoxBase::NotReady
878 || mObj->mState == VirtualBoxBase::InUninit;
879
880 if (mObj->mState == VirtualBoxBase::InitFailed)
881 {
882 /* we've been called by init() on failure */
883 mInitFailed = true;
884 }
885 else
886 {
887 if (mUninitDone)
888 {
889 /* do nothing if already uninitialized */
890 if (mObj->mState == VirtualBoxBase::NotReady)
891 return;
892
893 /* otherwise, wait until another thread finishes uninitialization.
894 * This is necessary to make sure that when this method returns, the
895 * object is NotReady and therefore can be deleted (for example). */
896
897 /* lazy semaphore creation */
898 if (mObj->mInitUninitSem == NIL_RTSEMEVENTMULTI)
899 {
900 RTSemEventMultiCreate(&mObj->mInitUninitSem);
901 Assert(mObj->mInitUninitWaiters == 0);
902 }
903 ++mObj->mInitUninitWaiters;
904
905 LogFlowFunc(("{%p}: Waiting for AutoUninitSpan to finish...\n",
906 mObj));
907
908 stateLock.release();
909 RTSemEventMultiWait(mObj->mInitUninitSem, RT_INDEFINITE_WAIT);
910 stateLock.acquire();
911
912 if (--mObj->mInitUninitWaiters == 0)
913 {
914 /* destroy the semaphore since no more necessary */
915 RTSemEventMultiDestroy(mObj->mInitUninitSem);
916 mObj->mInitUninitSem = NIL_RTSEMEVENTMULTI;
917 }
918
919 return;
920 }
921 }
922
923 /* go to InUninit to prevent from adding new callers */
924 mObj->setState(VirtualBoxBase::InUninit);
925
926 /* wait for already existing callers to drop to zero */
927 if (mObj->mCallers > 0)
928 {
929 /* lazy creation */
930 Assert(mObj->mZeroCallersSem == NIL_RTSEMEVENT);
931 RTSemEventCreate(&mObj->mZeroCallersSem);
932
933 /* wait until remaining callers release the object */
934 LogFlowFunc(("{%p}: Waiting for callers (%d) to drop to zero...\n",
935 mObj, mObj->mCallers));
936
937 stateLock.release();
938 RTSemEventWait(mObj->mZeroCallersSem, RT_INDEFINITE_WAIT);
939 }
940}
941
942/**
943 * Places the managed VirtualBoxBase object to the NotReady state.
944 */
945AutoUninitSpan::~AutoUninitSpan()
946{
947 /* do nothing if already uninitialized */
948 if (mUninitDone)
949 return;
950
951 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
952
953 Assert(mObj->mState == VirtualBoxBase::InUninit);
954
955 mObj->setState(VirtualBoxBase::NotReady);
956}
957
958////////////////////////////////////////////////////////////////////////////////
959//
960// MultiResult methods
961//
962////////////////////////////////////////////////////////////////////////////////
963
964RTTLS MultiResult::sCounter = NIL_RTTLS;
965
966/*static*/
967void MultiResult::incCounter()
968{
969 if (sCounter == NIL_RTTLS)
970 {
971 sCounter = RTTlsAlloc();
972 AssertReturnVoid(sCounter != NIL_RTTLS);
973 }
974
975 uintptr_t counter = (uintptr_t)RTTlsGet(sCounter);
976 ++counter;
977 RTTlsSet(sCounter, (void*)counter);
978}
979
980/*static*/
981void MultiResult::decCounter()
982{
983 uintptr_t counter = (uintptr_t)RTTlsGet(sCounter);
984 AssertReturnVoid(counter != 0);
985 --counter;
986 RTTlsSet(sCounter, (void*)counter);
987}
988
989/*static*/
990bool MultiResult::isMultiEnabled()
991{
992 if (sCounter == NIL_RTTLS)
993 return false;
994
995 return ((uintptr_t)RTTlsGet(MultiResult::sCounter)) > 0;
996}
997
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