VirtualBox

source: vbox/trunk/src/VBox/Main/VirtualBoxBase.cpp@ 35263

Last change on this file since 35263 was 34237, checked in by vboxsync, 14 years ago

Main: Be a bit more obvious when logging warnings instead of errors.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.5 KB
Line 
1/* $Id: VirtualBoxBase.cpp 34237 2010-11-22 13:24:35Z 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//
514// AutoInitSpan methods
515//
516////////////////////////////////////////////////////////////////////////////////
517
518/**
519 * Creates a smart initialization span object that places the object to
520 * InInit state.
521 *
522 * Please see the AutoInitSpan class description for more info.
523 *
524 * @param aObj |this| pointer of the managed VirtualBoxBase object whose
525 * init() method is being called.
526 * @param aResult Default initialization result.
527 */
528AutoInitSpan::AutoInitSpan(VirtualBoxBase *aObj,
529 Result aResult /* = Failed */)
530 : mObj(aObj),
531 mResult(aResult),
532 mOk(false)
533{
534 Assert(aObj);
535
536 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
537
538 mOk = mObj->mState == VirtualBoxBase::NotReady;
539 AssertReturnVoid (mOk);
540
541 mObj->setState(VirtualBoxBase::InInit);
542}
543
544/**
545 * Places the managed VirtualBoxBase object to Ready/Limited state if the
546 * initialization succeeded or partly succeeded, or places it to InitFailed
547 * state and calls the object's uninit() method.
548 *
549 * Please see the AutoInitSpan class description for more info.
550 */
551AutoInitSpan::~AutoInitSpan()
552{
553 /* if the state was other than NotReady, do nothing */
554 if (!mOk)
555 return;
556
557 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
558
559 Assert(mObj->mState == VirtualBoxBase::InInit);
560
561 if (mObj->mCallers > 0)
562 {
563 Assert(mObj->mInitUninitWaiters > 0);
564
565 /* We have some pending addCaller() calls on other threads (created
566 * during InInit), signal that InInit is finished and they may go on. */
567 RTSemEventMultiSignal(mObj->mInitUninitSem);
568 }
569
570 if (mResult == Succeeded)
571 {
572 mObj->setState(VirtualBoxBase::Ready);
573 }
574 else
575 if (mResult == Limited)
576 {
577 mObj->setState(VirtualBoxBase::Limited);
578 }
579 else
580 {
581 mObj->setState(VirtualBoxBase::InitFailed);
582 /* leave the lock to prevent nesting when uninit() is called */
583 stateLock.leave();
584 /* call uninit() to let the object uninit itself after failed init() */
585 mObj->uninit();
586 /* Note: the object may no longer exist here (for example, it can call
587 * the destructor in uninit()) */
588 }
589}
590
591// AutoReinitSpan methods
592////////////////////////////////////////////////////////////////////////////////
593
594/**
595 * Creates a smart re-initialization span object and places the object to
596 * InInit state.
597 *
598 * Please see the AutoInitSpan class description for more info.
599 *
600 * @param aObj |this| pointer of the managed VirtualBoxBase object whose
601 * re-initialization method is being called.
602 */
603AutoReinitSpan::AutoReinitSpan(VirtualBoxBase *aObj)
604 : mObj(aObj),
605 mSucceeded(false),
606 mOk(false)
607{
608 Assert(aObj);
609
610 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
611
612 mOk = mObj->mState == VirtualBoxBase::Limited;
613 AssertReturnVoid (mOk);
614
615 mObj->setState(VirtualBoxBase::InInit);
616}
617
618/**
619 * Places the managed VirtualBoxBase object to Ready state if the
620 * re-initialization succeeded (i.e. #setSucceeded() has been called) or back to
621 * Limited state otherwise.
622 *
623 * Please see the AutoInitSpan class description for more info.
624 */
625AutoReinitSpan::~AutoReinitSpan()
626{
627 /* if the state was other than Limited, do nothing */
628 if (!mOk)
629 return;
630
631 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
632
633 Assert(mObj->mState == VirtualBoxBase::InInit);
634
635 if (mObj->mCallers > 0 && mObj->mInitUninitWaiters > 0)
636 {
637 /* We have some pending addCaller() calls on other threads (created
638 * during InInit), signal that InInit is finished and they may go on. */
639 RTSemEventMultiSignal(mObj->mInitUninitSem);
640 }
641
642 if (mSucceeded)
643 {
644 mObj->setState(VirtualBoxBase::Ready);
645 }
646 else
647 {
648 mObj->setState(VirtualBoxBase::Limited);
649 }
650}
651
652// AutoUninitSpan methods
653////////////////////////////////////////////////////////////////////////////////
654
655/**
656 * Creates a smart uninitialization span object and places this object to
657 * InUninit state.
658 *
659 * Please see the AutoInitSpan class description for more info.
660 *
661 * @note This method blocks the current thread execution until the number of
662 * callers of the managed VirtualBoxBase object drops to zero!
663 *
664 * @param aObj |this| pointer of the VirtualBoxBase object whose uninit()
665 * method is being called.
666 */
667AutoUninitSpan::AutoUninitSpan(VirtualBoxBase *aObj)
668 : mObj(aObj),
669 mInitFailed(false),
670 mUninitDone(false)
671{
672 Assert(aObj);
673
674 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
675
676 Assert(mObj->mState != VirtualBoxBase::InInit);
677
678 /* Set mUninitDone to |true| if this object is already uninitialized
679 * (NotReady) or if another AutoUninitSpan is currently active on some
680 * other thread (InUninit). */
681 mUninitDone = mObj->mState == VirtualBoxBase::NotReady
682 || mObj->mState == VirtualBoxBase::InUninit;
683
684 if (mObj->mState == VirtualBoxBase::InitFailed)
685 {
686 /* we've been called by init() on failure */
687 mInitFailed = true;
688 }
689 else
690 {
691 if (mUninitDone)
692 {
693 /* do nothing if already uninitialized */
694 if (mObj->mState == VirtualBoxBase::NotReady)
695 return;
696
697 /* otherwise, wait until another thread finishes uninitialization.
698 * This is necessary to make sure that when this method returns, the
699 * object is NotReady and therefore can be deleted (for example). */
700
701 /* lazy semaphore creation */
702 if (mObj->mInitUninitSem == NIL_RTSEMEVENTMULTI)
703 {
704 RTSemEventMultiCreate(&mObj->mInitUninitSem);
705 Assert(mObj->mInitUninitWaiters == 0);
706 }
707 ++mObj->mInitUninitWaiters;
708
709 LogFlowFunc(("{%p}: Waiting for AutoUninitSpan to finish...\n",
710 mObj));
711
712 stateLock.leave();
713 RTSemEventMultiWait(mObj->mInitUninitSem, RT_INDEFINITE_WAIT);
714 stateLock.enter();
715
716 if (--mObj->mInitUninitWaiters == 0)
717 {
718 /* destroy the semaphore since no more necessary */
719 RTSemEventMultiDestroy(mObj->mInitUninitSem);
720 mObj->mInitUninitSem = NIL_RTSEMEVENTMULTI;
721 }
722
723 return;
724 }
725 }
726
727 /* go to InUninit to prevent from adding new callers */
728 mObj->setState(VirtualBoxBase::InUninit);
729
730 /* wait for already existing callers to drop to zero */
731 if (mObj->mCallers > 0)
732 {
733 /* lazy creation */
734 Assert(mObj->mZeroCallersSem == NIL_RTSEMEVENT);
735 RTSemEventCreate(&mObj->mZeroCallersSem);
736
737 /* wait until remaining callers release the object */
738 LogFlowFunc(("{%p}: Waiting for callers (%d) to drop to zero...\n",
739 mObj, mObj->mCallers));
740
741 stateLock.leave();
742 RTSemEventWait(mObj->mZeroCallersSem, RT_INDEFINITE_WAIT);
743 }
744}
745
746/**
747 * Places the managed VirtualBoxBase object to the NotReady state.
748 */
749AutoUninitSpan::~AutoUninitSpan()
750{
751 /* do nothing if already uninitialized */
752 if (mUninitDone)
753 return;
754
755 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
756
757 Assert(mObj->mState == VirtualBoxBase::InUninit);
758
759 mObj->setState(VirtualBoxBase::NotReady);
760}
761
762////////////////////////////////////////////////////////////////////////////////
763//
764// MultiResult methods
765//
766////////////////////////////////////////////////////////////////////////////////
767
768RTTLS MultiResult::sCounter = NIL_RTTLS;
769
770/*static*/
771void MultiResult::incCounter()
772{
773 if (sCounter == NIL_RTTLS)
774 {
775 sCounter = RTTlsAlloc();
776 AssertReturnVoid(sCounter != NIL_RTTLS);
777 }
778
779 uintptr_t counter = (uintptr_t)RTTlsGet(sCounter);
780 ++counter;
781 RTTlsSet(sCounter, (void*)counter);
782}
783
784/*static*/
785void MultiResult::decCounter()
786{
787 uintptr_t counter = (uintptr_t)RTTlsGet(sCounter);
788 AssertReturnVoid(counter != 0);
789 --counter;
790 RTTlsSet(sCounter, (void*)counter);
791}
792
793/*static*/
794bool MultiResult::isMultiEnabled()
795{
796 return ((uintptr_t)RTTlsGet(MultiResult::sCounter)) > 0;
797}
798
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use