VirtualBox

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

Last change on this file since 37608 was 36451, checked in by vboxsync, 14 years ago

introduced VirtualBoxBase::clearError to clear the previous error info if MultiResult is used

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.1 KB
Line 
1/* $Id: VirtualBoxBase.cpp 36451 2011-03-28 19:40:52Z vboxsync $ */
2
3/** @file
4 *
5 * VirtualBox COM base classes implementation
6 */
7
8/*
9 * Copyright (C) 2006-2010 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.leave();
212 RTSemEventMultiWait (mInitUninitSem, RT_INDEFINITE_WAIT);
213 stateLock.enter();
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 const 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 AssertReturn(!aText.isEmpty(), E_FAIL);
331
332 /* reset the error severity bit if it's a warning */
333 if (aWarning)
334 aResultCode &= ~0x80000000;
335
336 HRESULT rc = S_OK;
337
338 do
339 {
340 ComObjPtr<VirtualBoxErrorInfo> info;
341 rc = info.createObject();
342 if (FAILED(rc)) break;
343
344#if !defined (VBOX_WITH_XPCOM)
345
346 ComPtr<IVirtualBoxErrorInfo> curInfo;
347 if (preserve)
348 {
349 /* get the current error info if any */
350 ComPtr<IErrorInfo> err;
351 rc = ::GetErrorInfo (0, err.asOutParam());
352 if (FAILED(rc)) break;
353 rc = err.queryInterfaceTo(curInfo.asOutParam());
354 if (FAILED(rc))
355 {
356 /* create a IVirtualBoxErrorInfo wrapper for the native
357 * IErrorInfo object */
358 ComObjPtr<VirtualBoxErrorInfo> wrapper;
359 rc = wrapper.createObject();
360 if (SUCCEEDED(rc))
361 {
362 rc = wrapper->init (err);
363 if (SUCCEEDED(rc))
364 curInfo = wrapper;
365 }
366 }
367 }
368 /* On failure, curInfo will stay null */
369 Assert(SUCCEEDED(rc) || curInfo.isNull());
370
371 /* set the current error info and preserve the previous one if any */
372 rc = info->init(aResultCode, aIID, pcszComponent, aText, curInfo);
373 if (FAILED(rc)) break;
374
375 ComPtr<IErrorInfo> err;
376 rc = info.queryInterfaceTo(err.asOutParam());
377 if (SUCCEEDED(rc))
378 rc = ::SetErrorInfo (0, err);
379
380#else // !defined (VBOX_WITH_XPCOM)
381
382 nsCOMPtr <nsIExceptionService> es;
383 es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
384 if (NS_SUCCEEDED(rc))
385 {
386 nsCOMPtr <nsIExceptionManager> em;
387 rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
388 if (FAILED(rc)) break;
389
390 ComPtr<IVirtualBoxErrorInfo> curInfo;
391 if (preserve)
392 {
393 /* get the current error info if any */
394 ComPtr<nsIException> ex;
395 rc = em->GetCurrentException (ex.asOutParam());
396 if (FAILED(rc)) break;
397 rc = ex.queryInterfaceTo(curInfo.asOutParam());
398 if (FAILED(rc))
399 {
400 /* create a IVirtualBoxErrorInfo wrapper for the native
401 * nsIException object */
402 ComObjPtr<VirtualBoxErrorInfo> wrapper;
403 rc = wrapper.createObject();
404 if (SUCCEEDED(rc))
405 {
406 rc = wrapper->init (ex);
407 if (SUCCEEDED(rc))
408 curInfo = wrapper;
409 }
410 }
411 }
412 /* On failure, curInfo will stay null */
413 Assert(SUCCEEDED(rc) || curInfo.isNull());
414
415 /* set the current error info and preserve the previous one if any */
416 rc = info->init(aResultCode, aIID, pcszComponent, Bstr(aText), curInfo);
417 if (FAILED(rc)) break;
418
419 ComPtr<nsIException> ex;
420 rc = info.queryInterfaceTo(ex.asOutParam());
421 if (SUCCEEDED(rc))
422 rc = em->SetCurrentException (ex);
423 }
424 else if (rc == NS_ERROR_UNEXPECTED)
425 {
426 /*
427 * It is possible that setError() is being called by the object
428 * after the XPCOM shutdown sequence has been initiated
429 * (for example, when XPCOM releases all instances it internally
430 * references, which can cause object's FinalConstruct() and then
431 * uninit()). In this case, do_GetService() above will return
432 * NS_ERROR_UNEXPECTED and it doesn't actually make sense to
433 * set the exception (nobody will be able to read it).
434 */
435 LogWarningFunc(("Will not set an exception because nsIExceptionService is not available "
436 "(NS_ERROR_UNEXPECTED). XPCOM is being shutdown?\n"));
437 rc = NS_OK;
438 }
439
440#endif // !defined (VBOX_WITH_XPCOM)
441 }
442 while (0);
443
444 AssertComRC (rc);
445
446 return SUCCEEDED(rc) ? aResultCode : rc;
447}
448
449/**
450 * Shortcut instance method to calling the static setErrorInternal with the
451 * class interface ID and component name inserted correctly. This uses the
452 * virtual getClassIID() and getComponentName() methods which are automatically
453 * defined by the VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT macro.
454 * @param aResultCode
455 * @param pcsz
456 * @return
457 */
458HRESULT VirtualBoxBase::setError(HRESULT aResultCode, const char *pcsz, ...)
459{
460 va_list args;
461 va_start(args, pcsz);
462 HRESULT rc = setErrorInternal(aResultCode,
463 this->getClassIID(),
464 this->getComponentName(),
465 Utf8Str(pcsz, args),
466 false /* aWarning */,
467 true /* aLogIt */);
468 va_end(args);
469 return rc;
470}
471
472/**
473 * Like setError(), but sets the "warning" bit in the call to setErrorInternal().
474 * @param aResultCode
475 * @param pcsz
476 * @return
477 */
478HRESULT VirtualBoxBase::setWarning(HRESULT aResultCode, const char *pcsz, ...)
479{
480 va_list args;
481 va_start(args, pcsz);
482 HRESULT rc = setErrorInternal(aResultCode,
483 this->getClassIID(),
484 this->getComponentName(),
485 Utf8Str(pcsz, args),
486 true /* aWarning */,
487 true /* aLogIt */);
488 va_end(args);
489 return rc;
490}
491
492/**
493 * Like setError(), but disables the "log" flag in the call to setErrorInternal().
494 * @param aResultCode
495 * @param pcsz
496 * @return
497 */
498HRESULT VirtualBoxBase::setErrorNoLog(HRESULT aResultCode, const char *pcsz, ...)
499{
500 va_list args;
501 va_start(args, pcsz);
502 HRESULT rc = setErrorInternal(aResultCode,
503 this->getClassIID(),
504 this->getComponentName(),
505 Utf8Str(pcsz, args),
506 false /* aWarning */,
507 false /* aLogIt */);
508 va_end(args);
509 return rc;
510}
511
512/**
513 * Clear the current error information.
514 */
515/*static*/
516void VirtualBoxBase::clearError(void)
517{
518#if !defined(VBOX_WITH_XPCOM)
519 ::SetErrorInfo (0, NULL);
520#else
521 HRESULT rc = S_OK;
522 nsCOMPtr <nsIExceptionService> es;
523 es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
524 if (NS_SUCCEEDED(rc))
525 {
526 nsCOMPtr <nsIExceptionManager> em;
527 rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
528 if (SUCCEEDED(rc))
529 em->SetCurrentException(NULL);
530 }
531#endif
532}
533
534
535////////////////////////////////////////////////////////////////////////////////
536//
537// AutoInitSpan methods
538//
539////////////////////////////////////////////////////////////////////////////////
540
541/**
542 * Creates a smart initialization span object that places the object to
543 * InInit state.
544 *
545 * Please see the AutoInitSpan class description for more info.
546 *
547 * @param aObj |this| pointer of the managed VirtualBoxBase object whose
548 * init() method is being called.
549 * @param aResult Default initialization result.
550 */
551AutoInitSpan::AutoInitSpan(VirtualBoxBase *aObj,
552 Result aResult /* = Failed */)
553 : mObj(aObj),
554 mResult(aResult),
555 mOk(false)
556{
557 Assert(aObj);
558
559 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
560
561 mOk = mObj->mState == VirtualBoxBase::NotReady;
562 AssertReturnVoid (mOk);
563
564 mObj->setState(VirtualBoxBase::InInit);
565}
566
567/**
568 * Places the managed VirtualBoxBase object to Ready/Limited state if the
569 * initialization succeeded or partly succeeded, or places it to InitFailed
570 * state and calls the object's uninit() method.
571 *
572 * Please see the AutoInitSpan class description for more info.
573 */
574AutoInitSpan::~AutoInitSpan()
575{
576 /* if the state was other than NotReady, do nothing */
577 if (!mOk)
578 return;
579
580 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
581
582 Assert(mObj->mState == VirtualBoxBase::InInit);
583
584 if (mObj->mCallers > 0)
585 {
586 Assert(mObj->mInitUninitWaiters > 0);
587
588 /* We have some pending addCaller() calls on other threads (created
589 * during InInit), signal that InInit is finished and they may go on. */
590 RTSemEventMultiSignal(mObj->mInitUninitSem);
591 }
592
593 if (mResult == Succeeded)
594 {
595 mObj->setState(VirtualBoxBase::Ready);
596 }
597 else
598 if (mResult == Limited)
599 {
600 mObj->setState(VirtualBoxBase::Limited);
601 }
602 else
603 {
604 mObj->setState(VirtualBoxBase::InitFailed);
605 /* leave the lock to prevent nesting when uninit() is called */
606 stateLock.leave();
607 /* call uninit() to let the object uninit itself after failed init() */
608 mObj->uninit();
609 /* Note: the object may no longer exist here (for example, it can call
610 * the destructor in uninit()) */
611 }
612}
613
614// AutoReinitSpan methods
615////////////////////////////////////////////////////////////////////////////////
616
617/**
618 * Creates a smart re-initialization span object and places the object to
619 * InInit state.
620 *
621 * Please see the AutoInitSpan class description for more info.
622 *
623 * @param aObj |this| pointer of the managed VirtualBoxBase object whose
624 * re-initialization method is being called.
625 */
626AutoReinitSpan::AutoReinitSpan(VirtualBoxBase *aObj)
627 : mObj(aObj),
628 mSucceeded(false),
629 mOk(false)
630{
631 Assert(aObj);
632
633 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
634
635 mOk = mObj->mState == VirtualBoxBase::Limited;
636 AssertReturnVoid (mOk);
637
638 mObj->setState(VirtualBoxBase::InInit);
639}
640
641/**
642 * Places the managed VirtualBoxBase object to Ready state if the
643 * re-initialization succeeded (i.e. #setSucceeded() has been called) or back to
644 * Limited state otherwise.
645 *
646 * Please see the AutoInitSpan class description for more info.
647 */
648AutoReinitSpan::~AutoReinitSpan()
649{
650 /* if the state was other than Limited, do nothing */
651 if (!mOk)
652 return;
653
654 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
655
656 Assert(mObj->mState == VirtualBoxBase::InInit);
657
658 if (mObj->mCallers > 0 && mObj->mInitUninitWaiters > 0)
659 {
660 /* We have some pending addCaller() calls on other threads (created
661 * during InInit), signal that InInit is finished and they may go on. */
662 RTSemEventMultiSignal(mObj->mInitUninitSem);
663 }
664
665 if (mSucceeded)
666 {
667 mObj->setState(VirtualBoxBase::Ready);
668 }
669 else
670 {
671 mObj->setState(VirtualBoxBase::Limited);
672 }
673}
674
675// AutoUninitSpan methods
676////////////////////////////////////////////////////////////////////////////////
677
678/**
679 * Creates a smart uninitialization span object and places this object to
680 * InUninit state.
681 *
682 * Please see the AutoInitSpan class description for more info.
683 *
684 * @note This method blocks the current thread execution until the number of
685 * callers of the managed VirtualBoxBase object drops to zero!
686 *
687 * @param aObj |this| pointer of the VirtualBoxBase object whose uninit()
688 * method is being called.
689 */
690AutoUninitSpan::AutoUninitSpan(VirtualBoxBase *aObj)
691 : mObj(aObj),
692 mInitFailed(false),
693 mUninitDone(false)
694{
695 Assert(aObj);
696
697 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
698
699 Assert(mObj->mState != VirtualBoxBase::InInit);
700
701 /* Set mUninitDone to |true| if this object is already uninitialized
702 * (NotReady) or if another AutoUninitSpan is currently active on some
703 * other thread (InUninit). */
704 mUninitDone = mObj->mState == VirtualBoxBase::NotReady
705 || mObj->mState == VirtualBoxBase::InUninit;
706
707 if (mObj->mState == VirtualBoxBase::InitFailed)
708 {
709 /* we've been called by init() on failure */
710 mInitFailed = true;
711 }
712 else
713 {
714 if (mUninitDone)
715 {
716 /* do nothing if already uninitialized */
717 if (mObj->mState == VirtualBoxBase::NotReady)
718 return;
719
720 /* otherwise, wait until another thread finishes uninitialization.
721 * This is necessary to make sure that when this method returns, the
722 * object is NotReady and therefore can be deleted (for example). */
723
724 /* lazy semaphore creation */
725 if (mObj->mInitUninitSem == NIL_RTSEMEVENTMULTI)
726 {
727 RTSemEventMultiCreate(&mObj->mInitUninitSem);
728 Assert(mObj->mInitUninitWaiters == 0);
729 }
730 ++mObj->mInitUninitWaiters;
731
732 LogFlowFunc(("{%p}: Waiting for AutoUninitSpan to finish...\n",
733 mObj));
734
735 stateLock.leave();
736 RTSemEventMultiWait(mObj->mInitUninitSem, RT_INDEFINITE_WAIT);
737 stateLock.enter();
738
739 if (--mObj->mInitUninitWaiters == 0)
740 {
741 /* destroy the semaphore since no more necessary */
742 RTSemEventMultiDestroy(mObj->mInitUninitSem);
743 mObj->mInitUninitSem = NIL_RTSEMEVENTMULTI;
744 }
745
746 return;
747 }
748 }
749
750 /* go to InUninit to prevent from adding new callers */
751 mObj->setState(VirtualBoxBase::InUninit);
752
753 /* wait for already existing callers to drop to zero */
754 if (mObj->mCallers > 0)
755 {
756 /* lazy creation */
757 Assert(mObj->mZeroCallersSem == NIL_RTSEMEVENT);
758 RTSemEventCreate(&mObj->mZeroCallersSem);
759
760 /* wait until remaining callers release the object */
761 LogFlowFunc(("{%p}: Waiting for callers (%d) to drop to zero...\n",
762 mObj, mObj->mCallers));
763
764 stateLock.leave();
765 RTSemEventWait(mObj->mZeroCallersSem, RT_INDEFINITE_WAIT);
766 }
767}
768
769/**
770 * Places the managed VirtualBoxBase object to the NotReady state.
771 */
772AutoUninitSpan::~AutoUninitSpan()
773{
774 /* do nothing if already uninitialized */
775 if (mUninitDone)
776 return;
777
778 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
779
780 Assert(mObj->mState == VirtualBoxBase::InUninit);
781
782 mObj->setState(VirtualBoxBase::NotReady);
783}
784
785////////////////////////////////////////////////////////////////////////////////
786//
787// MultiResult methods
788//
789////////////////////////////////////////////////////////////////////////////////
790
791RTTLS MultiResult::sCounter = NIL_RTTLS;
792
793/*static*/
794void MultiResult::incCounter()
795{
796 if (sCounter == NIL_RTTLS)
797 {
798 sCounter = RTTlsAlloc();
799 AssertReturnVoid(sCounter != NIL_RTTLS);
800 }
801
802 uintptr_t counter = (uintptr_t)RTTlsGet(sCounter);
803 ++counter;
804 RTTlsSet(sCounter, (void*)counter);
805}
806
807/*static*/
808void MultiResult::decCounter()
809{
810 uintptr_t counter = (uintptr_t)RTTlsGet(sCounter);
811 AssertReturnVoid(counter != 0);
812 --counter;
813 RTTlsSet(sCounter, (void*)counter);
814}
815
816/*static*/
817bool MultiResult::isMultiEnabled()
818{
819 if (sCounter == NIL_RTTLS)
820 return false;
821
822 return ((uintptr_t)RTTlsGet(MultiResult::sCounter)) > 0;
823}
824
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