VirtualBox

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

Last change on this file since 30760 was 30739, checked in by vboxsync, 14 years ago

Main: remove VirtualBoxSupportTranslation template, add translation support to generic base class, clean up COM headers more, remove SupportErrorInfo.cpp|h

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.9 KB
Line 
1/* $Id: VirtualBoxBase.cpp 30739 2010-07-08 12:27:42Z 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 guaranted 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 succeeeds (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 * succeeed 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 overriden 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 preferrable 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(("ERROR [COM]: aRC=%Rhrc (%#08x) aIID={%RTuuid} aComponent={%s} aText={%s} aWarning=%RTbool, preserve=%RTbool\n",
317 aResultCode,
318 aResultCode,
319 &aIID,
320 pcszComponent,
321 aText.c_str(),
322 aWarning,
323 preserve));
324
325 /* these are mandatory, others -- not */
326 AssertReturn((!aWarning && FAILED(aResultCode)) ||
327 (aWarning && aResultCode != S_OK),
328 E_FAIL);
329 AssertReturn(!aText.isEmpty(), 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 do
338 {
339 ComObjPtr<VirtualBoxErrorInfo> info;
340 rc = info.createObject();
341 if (FAILED(rc)) break;
342
343#if !defined (VBOX_WITH_XPCOM)
344
345 ComPtr<IVirtualBoxErrorInfo> curInfo;
346 if (preserve)
347 {
348 /* get the current error info if any */
349 ComPtr<IErrorInfo> err;
350 rc = ::GetErrorInfo (0, err.asOutParam());
351 if (FAILED(rc)) break;
352 rc = err.queryInterfaceTo(curInfo.asOutParam());
353 if (FAILED(rc))
354 {
355 /* create a IVirtualBoxErrorInfo wrapper for the native
356 * IErrorInfo object */
357 ComObjPtr<VirtualBoxErrorInfo> wrapper;
358 rc = wrapper.createObject();
359 if (SUCCEEDED(rc))
360 {
361 rc = wrapper->init (err);
362 if (SUCCEEDED(rc))
363 curInfo = wrapper;
364 }
365 }
366 }
367 /* On failure, curInfo will stay null */
368 Assert(SUCCEEDED(rc) || curInfo.isNull());
369
370 /* set the current error info and preserve the previous one if any */
371 rc = info->init(aResultCode, aIID, pcszComponent, aText, curInfo);
372 if (FAILED(rc)) break;
373
374 ComPtr<IErrorInfo> err;
375 rc = info.queryInterfaceTo(err.asOutParam());
376 if (SUCCEEDED(rc))
377 rc = ::SetErrorInfo (0, err);
378
379#else // !defined (VBOX_WITH_XPCOM)
380
381 nsCOMPtr <nsIExceptionService> es;
382 es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
383 if (NS_SUCCEEDED(rc))
384 {
385 nsCOMPtr <nsIExceptionManager> em;
386 rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
387 if (FAILED(rc)) break;
388
389 ComPtr<IVirtualBoxErrorInfo> curInfo;
390 if (preserve)
391 {
392 /* get the current error info if any */
393 ComPtr<nsIException> ex;
394 rc = em->GetCurrentException (ex.asOutParam());
395 if (FAILED(rc)) break;
396 rc = ex.queryInterfaceTo(curInfo.asOutParam());
397 if (FAILED(rc))
398 {
399 /* create a IVirtualBoxErrorInfo wrapper for the native
400 * nsIException object */
401 ComObjPtr<VirtualBoxErrorInfo> wrapper;
402 rc = wrapper.createObject();
403 if (SUCCEEDED(rc))
404 {
405 rc = wrapper->init (ex);
406 if (SUCCEEDED(rc))
407 curInfo = wrapper;
408 }
409 }
410 }
411 /* On failure, curInfo will stay null */
412 Assert(SUCCEEDED(rc) || curInfo.isNull());
413
414 /* set the current error info and preserve the previous one if any */
415 rc = info->init(aResultCode, aIID, pcszComponent, Bstr(aText), curInfo);
416 if (FAILED(rc)) break;
417
418 ComPtr<nsIException> ex;
419 rc = info.queryInterfaceTo(ex.asOutParam());
420 if (SUCCEEDED(rc))
421 rc = em->SetCurrentException (ex);
422 }
423 else if (rc == NS_ERROR_UNEXPECTED)
424 {
425 /*
426 * It is possible that setError() is being called by the object
427 * after the XPCOM shutdown sequence has been initiated
428 * (for example, when XPCOM releases all instances it internally
429 * references, which can cause object's FinalConstruct() and then
430 * uninit()). In this case, do_GetService() above will return
431 * NS_ERROR_UNEXPECTED and it doesn't actually make sense to
432 * set the exception (nobody will be able to read it).
433 */
434 LogWarningFunc(("Will not set an exception because nsIExceptionService is not available "
435 "(NS_ERROR_UNEXPECTED). XPCOM is being shutdown?\n"));
436 rc = NS_OK;
437 }
438
439#endif // !defined (VBOX_WITH_XPCOM)
440 }
441 while (0);
442
443 AssertComRC (rc);
444
445 return SUCCEEDED(rc) ? aResultCode : rc;
446}
447
448/**
449 * Shortcut instance method to calling the static setErrorInternal with the
450 * class interface ID and component name inserted correctly. This uses the
451 * virtual getClassIID() and getComponentName() methods which are automatically
452 * defined by the VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT macro.
453 * @param aResultCode
454 * @param pcsz
455 * @return
456 */
457HRESULT VirtualBoxBase::setError(HRESULT aResultCode, const char *pcsz, ...)
458{
459 va_list args;
460 va_start(args, pcsz);
461 HRESULT rc = setErrorInternal(aResultCode,
462 this->getClassIID(),
463 this->getComponentName(),
464 Utf8StrFmtVA(pcsz, args),
465 false /* aWarning */,
466 true /* aLogIt */);
467 va_end(args);
468 return rc;
469}
470
471/**
472 * Like setError(), but sets the "warning" bit in the call to setErrorInternal().
473 * @param aResultCode
474 * @param pcsz
475 * @return
476 */
477HRESULT VirtualBoxBase::setWarning(HRESULT aResultCode, const char *pcsz, ...)
478{
479 va_list args;
480 va_start(args, pcsz);
481 HRESULT rc = setErrorInternal(aResultCode,
482 this->getClassIID(),
483 this->getComponentName(),
484 Utf8StrFmtVA(pcsz, args),
485 true /* aWarning */,
486 true /* aLogIt */);
487 va_end(args);
488 return rc;
489}
490
491/**
492 * Like setError(), but disables the "log" flag in the call to setErrorInternal().
493 * @param aResultCode
494 * @param pcsz
495 * @return
496 */
497HRESULT VirtualBoxBase::setErrorNoLog(HRESULT aResultCode, const char *pcsz, ...)
498{
499 va_list args;
500 va_start(args, pcsz);
501 HRESULT rc = setErrorInternal(aResultCode,
502 this->getClassIID(),
503 this->getComponentName(),
504 Utf8StrFmtVA(pcsz, args),
505 false /* aWarning */,
506 false /* aLogIt */);
507 va_end(args);
508 return rc;
509}
510
511////////////////////////////////////////////////////////////////////////////////
512//
513// AutoInitSpan methods
514//
515////////////////////////////////////////////////////////////////////////////////
516
517/**
518 * Creates a smart initialization span object that places the object to
519 * InInit state.
520 *
521 * Please see the AutoInitSpan class description for more info.
522 *
523 * @param aObj |this| pointer of the managed VirtualBoxBase object whose
524 * init() method is being called.
525 * @param aResult Default initialization result.
526 */
527AutoInitSpan::AutoInitSpan(VirtualBoxBase *aObj,
528 Result aResult /* = Failed */)
529 : mObj(aObj),
530 mResult(aResult),
531 mOk(false)
532{
533 Assert(aObj);
534
535 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
536
537 mOk = mObj->mState == VirtualBoxBase::NotReady;
538 AssertReturnVoid (mOk);
539
540 mObj->setState(VirtualBoxBase::InInit);
541}
542
543/**
544 * Places the managed VirtualBoxBase object to Ready/Limited state if the
545 * initialization succeeded or partly succeeded, or places it to InitFailed
546 * state and calls the object's uninit() method.
547 *
548 * Please see the AutoInitSpan class description for more info.
549 */
550AutoInitSpan::~AutoInitSpan()
551{
552 /* if the state was other than NotReady, do nothing */
553 if (!mOk)
554 return;
555
556 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
557
558 Assert(mObj->mState == VirtualBoxBase::InInit);
559
560 if (mObj->mCallers > 0)
561 {
562 Assert(mObj->mInitUninitWaiters > 0);
563
564 /* We have some pending addCaller() calls on other threads (created
565 * during InInit), signal that InInit is finished and they may go on. */
566 RTSemEventMultiSignal(mObj->mInitUninitSem);
567 }
568
569 if (mResult == Succeeded)
570 {
571 mObj->setState(VirtualBoxBase::Ready);
572 }
573 else
574 if (mResult == Limited)
575 {
576 mObj->setState(VirtualBoxBase::Limited);
577 }
578 else
579 {
580 mObj->setState(VirtualBoxBase::InitFailed);
581 /* leave the lock to prevent nesting when uninit() is called */
582 stateLock.leave();
583 /* call uninit() to let the object uninit itself after failed init() */
584 mObj->uninit();
585 /* Note: the object may no longer exist here (for example, it can call
586 * the destructor in uninit()) */
587 }
588}
589
590// AutoReinitSpan methods
591////////////////////////////////////////////////////////////////////////////////
592
593/**
594 * Creates a smart re-initialization span object and places the object to
595 * InInit state.
596 *
597 * Please see the AutoInitSpan class description for more info.
598 *
599 * @param aObj |this| pointer of the managed VirtualBoxBase object whose
600 * re-initialization method is being called.
601 */
602AutoReinitSpan::AutoReinitSpan(VirtualBoxBase *aObj)
603 : mObj(aObj),
604 mSucceeded(false),
605 mOk(false)
606{
607 Assert(aObj);
608
609 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
610
611 mOk = mObj->mState == VirtualBoxBase::Limited;
612 AssertReturnVoid (mOk);
613
614 mObj->setState(VirtualBoxBase::InInit);
615}
616
617/**
618 * Places the managed VirtualBoxBase object to Ready state if the
619 * re-initialization succeeded (i.e. #setSucceeded() has been called) or back to
620 * Limited state otherwise.
621 *
622 * Please see the AutoInitSpan class description for more info.
623 */
624AutoReinitSpan::~AutoReinitSpan()
625{
626 /* if the state was other than Limited, do nothing */
627 if (!mOk)
628 return;
629
630 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
631
632 Assert(mObj->mState == VirtualBoxBase::InInit);
633
634 if (mObj->mCallers > 0 && mObj->mInitUninitWaiters > 0)
635 {
636 /* We have some pending addCaller() calls on other threads (created
637 * during InInit), signal that InInit is finished and they may go on. */
638 RTSemEventMultiSignal(mObj->mInitUninitSem);
639 }
640
641 if (mSucceeded)
642 {
643 mObj->setState(VirtualBoxBase::Ready);
644 }
645 else
646 {
647 mObj->setState(VirtualBoxBase::Limited);
648 }
649}
650
651// AutoUninitSpan methods
652////////////////////////////////////////////////////////////////////////////////
653
654/**
655 * Creates a smart uninitialization span object and places this object to
656 * InUninit state.
657 *
658 * Please see the AutoInitSpan class description for more info.
659 *
660 * @note This method blocks the current thread execution until the number of
661 * callers of the managed VirtualBoxBase object drops to zero!
662 *
663 * @param aObj |this| pointer of the VirtualBoxBase object whose uninit()
664 * method is being called.
665 */
666AutoUninitSpan::AutoUninitSpan(VirtualBoxBase *aObj)
667 : mObj(aObj),
668 mInitFailed(false),
669 mUninitDone(false)
670{
671 Assert(aObj);
672
673 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
674
675 Assert(mObj->mState != VirtualBoxBase::InInit);
676
677 /* Set mUninitDone to |true| if this object is already uninitialized
678 * (NotReady) or if another AutoUninitSpan is currently active on some
679 * other thread (InUninit). */
680 mUninitDone = mObj->mState == VirtualBoxBase::NotReady
681 || mObj->mState == VirtualBoxBase::InUninit;
682
683 if (mObj->mState == VirtualBoxBase::InitFailed)
684 {
685 /* we've been called by init() on failure */
686 mInitFailed = true;
687 }
688 else
689 {
690 if (mUninitDone)
691 {
692 /* do nothing if already uninitialized */
693 if (mObj->mState == VirtualBoxBase::NotReady)
694 return;
695
696 /* otherwise, wait until another thread finishes uninitialization.
697 * This is necessary to make sure that when this method returns, the
698 * object is NotReady and therefore can be deleted (for example).
699 * In particular, this is used by
700 * VirtualBoxBaseWithTypedChildrenNEXT::uninitDependentChildren(). */
701
702 /* lazy semaphore creation */
703 if (mObj->mInitUninitSem == NIL_RTSEMEVENTMULTI)
704 {
705 RTSemEventMultiCreate(&mObj->mInitUninitSem);
706 Assert(mObj->mInitUninitWaiters == 0);
707 }
708 ++mObj->mInitUninitWaiters;
709
710 LogFlowFunc(("{%p}: Waiting for AutoUninitSpan to finish...\n",
711 mObj));
712
713 stateLock.leave();
714 RTSemEventMultiWait(mObj->mInitUninitSem, RT_INDEFINITE_WAIT);
715 stateLock.enter();
716
717 if (--mObj->mInitUninitWaiters == 0)
718 {
719 /* destroy the semaphore since no more necessary */
720 RTSemEventMultiDestroy(mObj->mInitUninitSem);
721 mObj->mInitUninitSem = NIL_RTSEMEVENTMULTI;
722 }
723
724 return;
725 }
726 }
727
728 /* go to InUninit to prevent from adding new callers */
729 mObj->setState(VirtualBoxBase::InUninit);
730
731 /* wait for already existing callers to drop to zero */
732 if (mObj->mCallers > 0)
733 {
734 /* lazy creation */
735 Assert(mObj->mZeroCallersSem == NIL_RTSEMEVENT);
736 RTSemEventCreate(&mObj->mZeroCallersSem);
737
738 /* wait until remaining callers release the object */
739 LogFlowFunc(("{%p}: Waiting for callers (%d) to drop to zero...\n",
740 mObj, mObj->mCallers));
741
742 stateLock.leave();
743 RTSemEventWait(mObj->mZeroCallersSem, RT_INDEFINITE_WAIT);
744 }
745}
746
747/**
748 * Places the managed VirtualBoxBase object to the NotReady state.
749 */
750AutoUninitSpan::~AutoUninitSpan()
751{
752 /* do nothing if already uninitialized */
753 if (mUninitDone)
754 return;
755
756 AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
757
758 Assert(mObj->mState == VirtualBoxBase::InUninit);
759
760 mObj->setState(VirtualBoxBase::NotReady);
761}
762
763////////////////////////////////////////////////////////////////////////////////
764//
765// VirtualBoxBaseWithChildrenNEXT methods
766//
767////////////////////////////////////////////////////////////////////////////////
768
769/**
770 * Uninitializes all dependent children registered on this object with
771 * #addDependentChild().
772 *
773 * Must be called from within the AutoUninitSpan (i.e.
774 * typically from this object's uninit() method) to uninitialize children
775 * before this object goes out of service and becomes unusable.
776 *
777 * Note that this method will call uninit() methods of child objects. If
778 * these methods need to call the parent object during uninitialization,
779 * #uninitDependentChildren() must be called before the relevant part of the
780 * parent is uninitialized: usually at the begnning of the parent
781 * uninitialization sequence.
782 *
783 * Keep in mind that the uninitialized child objects may be no longer available
784 * (i.e. may be deleted) after this method returns.
785 *
786 * @note Locks #childrenLock() for writing.
787 *
788 * @note May lock something else through the called children.
789 */
790void VirtualBoxBaseWithChildrenNEXT::uninitDependentChildren()
791{
792 AutoCaller autoCaller(this);
793
794 /* sanity */
795 AssertReturnVoid (autoCaller.state() == InUninit ||
796 autoCaller.state() == InInit);
797
798 AutoWriteLock chLock(childrenLock() COMMA_LOCKVAL_SRC_POS);
799
800 size_t count = mDependentChildren.size();
801
802 while (count != 0)
803 {
804 /* strongly reference the weak child from the map to make sure it won't
805 * be deleted while we've released the lock */
806 DependentChildren::iterator it = mDependentChildren.begin();
807 ComPtr<IUnknown> unk = it->first;
808 Assert(!unk.isNull());
809
810 VirtualBoxBase *child = it->second;
811
812 /* release the lock to let children stuck in removeDependentChild() go
813 * on (otherwise we'll deadlock in uninit() */
814 chLock.leave();
815
816 /* Note that if child->uninit() happens to be called on another
817 * thread right before us and is not yet finished, the second
818 * uninit() call will wait until the first one has done so
819 * (thanks to AutoUninitSpan). */
820 Assert(child);
821 if (child)
822 child->uninit();
823
824 chLock.enter();
825
826 /* uninit() is guaranteed to be done here so the child must be already
827 * deleted from the list by removeDependentChild() called from there.
828 * Do some checks to avoid endless loops when the user is forgetful */
829 -- count;
830 Assert(count == mDependentChildren.size());
831 if (count != mDependentChildren.size())
832 mDependentChildren.erase (it);
833
834 Assert(count == mDependentChildren.size());
835 }
836}
837
838/**
839 * Returns a pointer to the dependent child (registered using
840 * #addDependentChild()) corresponding to the given interface pointer or NULL if
841 * the given pointer is unrelated.
842 *
843 * The relation is checked by using the given interface pointer as a key in the
844 * map of dependent children.
845 *
846 * Note that ComPtr<IUnknown> is used as an argument instead of IUnknown * in
847 * order to guarantee IUnknown identity and disambiguation by doing
848 * QueryInterface (IUnknown) rather than a regular C cast.
849 *
850 * @param aUnk Pointer to map to the dependent child object.
851 * @return Pointer to the dependent VirtualBoxBase child object.
852 *
853 * @note Locks #childrenLock() for reading.
854 */
855VirtualBoxBase* VirtualBoxBaseWithChildrenNEXT::getDependentChild(const ComPtr<IUnknown> &aUnk)
856{
857 AssertReturn(!aUnk.isNull(), NULL);
858
859 AutoCaller autoCaller(this);
860
861 /* return NULL if uninitDependentChildren() is in action */
862 if (autoCaller.state() == InUninit)
863 return NULL;
864
865 AutoReadLock alock(childrenLock() COMMA_LOCKVAL_SRC_POS);
866
867 DependentChildren::const_iterator it = mDependentChildren.find (aUnk);
868 if (it == mDependentChildren.end())
869 return NULL;
870
871 return (*it).second;
872}
873
874/** Helper for addDependentChild(). */
875void VirtualBoxBaseWithChildrenNEXT::doAddDependentChild(IUnknown *aUnk,
876 VirtualBoxBase *aChild)
877{
878 AssertReturnVoid (aUnk != NULL);
879 AssertReturnVoid (aChild != NULL);
880
881 AutoCaller autoCaller(this);
882
883 /* sanity */
884 AssertReturnVoid (autoCaller.state() == InInit ||
885 autoCaller.state() == Ready ||
886 autoCaller.state() == Limited);
887
888 AutoWriteLock alock(childrenLock() COMMA_LOCKVAL_SRC_POS);
889
890 std::pair <DependentChildren::iterator, bool> result =
891 mDependentChildren.insert (DependentChildren::value_type (aUnk, aChild));
892 AssertMsg (result.second, ("Failed to insert child %p to the map\n", aUnk));
893}
894
895/** Helper for removeDependentChild(). */
896void VirtualBoxBaseWithChildrenNEXT::doRemoveDependentChild (IUnknown *aUnk)
897{
898 AssertReturnVoid (aUnk);
899
900 AutoCaller autoCaller(this);
901
902 /* sanity */
903 AssertReturnVoid (autoCaller.state() == InUninit ||
904 autoCaller.state() == InInit ||
905 autoCaller.state() == Ready ||
906 autoCaller.state() == Limited);
907
908 AutoWriteLock alock(childrenLock() COMMA_LOCKVAL_SRC_POS);
909
910 DependentChildren::size_type result = mDependentChildren.erase (aUnk);
911 AssertMsg (result == 1, ("Failed to remove child %p from the map\n", aUnk));
912 NOREF (result);
913}
914
915////////////////////////////////////////////////////////////////////////////////
916//
917// MultiResult methods
918//
919////////////////////////////////////////////////////////////////////////////////
920
921RTTLS MultiResult::sCounter = NIL_RTTLS;
922
923/*static*/
924void MultiResult::incCounter()
925{
926 if (sCounter == NIL_RTTLS)
927 {
928 sCounter = RTTlsAlloc();
929 AssertReturnVoid(sCounter != NIL_RTTLS);
930 }
931
932 uintptr_t counter = (uintptr_t)RTTlsGet(sCounter);
933 ++counter;
934 RTTlsSet(sCounter, (void*)counter);
935}
936
937/*static*/
938void MultiResult::decCounter()
939{
940 uintptr_t counter = (uintptr_t)RTTlsGet(sCounter);
941 AssertReturnVoid(counter != 0);
942 --counter;
943 RTTlsSet(sCounter, (void*)counter);
944}
945
946/*static*/
947bool MultiResult::isMultiEnabled()
948{
949 return ((uintptr_t)RTTlsGet(MultiResult::sCounter)) > 0;
950}
951
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