VirtualBox

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

Last change on this file since 8665 was 8665, checked in by vboxsync, 17 years ago

Use must use atomic compare-and-exchange logic here to be 100% correct.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.9 KB
Line 
1/* $Id: VirtualBoxBase.cpp 8665 2008-05-07 15:23:25Z vboxsync $ */
2
3/** @file
4 *
5 * VirtualBox COM base classes implementation
6 */
7
8/*
9 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.215389.xyz. 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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24#if !defined (VBOX_WITH_XPCOM)
25#include <windows.h>
26#include <dbghelp.h>
27#else /* !defined (VBOX_WITH_XPCOM) */
28#include <nsIServiceManager.h>
29#include <nsIExceptionService.h>
30#endif /* !defined (VBOX_WITH_XPCOM) */
31
32#include "VirtualBoxBase.h"
33#include "VirtualBoxErrorInfoImpl.h"
34#include "Logging.h"
35
36#include <iprt/semaphore.h>
37
38// VirtualBoxBaseNEXT_base methods
39////////////////////////////////////////////////////////////////////////////////
40
41VirtualBoxBaseNEXT_base::VirtualBoxBaseNEXT_base()
42{
43 mState = NotReady;
44 mStateChangeThread = NIL_RTTHREAD;
45 mCallers = 0;
46 mZeroCallersSem = NIL_RTSEMEVENT;
47 mInitDoneSem = NIL_RTSEMEVENTMULTI;
48 mInitDoneSemUsers = 0;
49 mObjectLock = NULL;
50}
51
52VirtualBoxBaseNEXT_base::~VirtualBoxBaseNEXT_base()
53{
54 if (mObjectLock)
55 delete mObjectLock;
56 Assert (mInitDoneSemUsers == 0);
57 Assert (mInitDoneSem == NIL_RTSEMEVENTMULTI);
58 if (mZeroCallersSem != NIL_RTSEMEVENT)
59 RTSemEventDestroy (mZeroCallersSem);
60 mCallers = 0;
61 mStateChangeThread = NIL_RTTHREAD;
62 mState = NotReady;
63}
64
65// util::Lockable interface
66RWLockHandle *VirtualBoxBaseNEXT_base::lockHandle() const
67{
68 /* lazy initialization */
69 if (RT_UNLIKELY(!mObjectLock))
70 {
71 AssertCompile (sizeof (RWLockHandle *) == sizeof (void *));
72 RWLockHandle *objLock = new RWLockHandle;
73 if (!ASMAtomicCmpXchgPtr ((void * volatile *) &mObjectLock, objLock, NULL))
74 {
75 delete objLock;
76 objLock = (RWLockHandle *) ASMAtomicReadPtr ((void * volatile *) &mObjectLock);
77 }
78 return objLock;
79 }
80 return mObjectLock;
81}
82
83/**
84 * Increments the number of calls to this object by one.
85 *
86 * After this method succeeds, it is guaranted that the object will remain in
87 * the Ready (or in the Limited) state at least until #releaseCaller() is
88 * called.
89 *
90 * This method is intended to mark the beginning of sections of code within
91 * methods of COM objects that depend on the readiness (Ready) state. The
92 * Ready state is a primary "ready to serve" state. Usually all code that
93 * works with component's data depends on it. On practice, this means that
94 * almost every public method, setter or getter of the object should add
95 * itself as an object's caller at the very beginning, to protect from an
96 * unexpected uninitialization that may happen on a different thread.
97 *
98 * Besides the Ready state denoting that the object is fully functional,
99 * there is a special Limited state. The Limited state means that the object
100 * is still functional, but its functionality is limited to some degree, so
101 * not all operations are possible. The @a aLimited argument to this method
102 * determines whether the caller represents this limited functionality or not.
103 *
104 * This method succeeeds (and increments the number of callers) only if the
105 * current object's state is Ready. Otherwise, it will return E_UNEXPECTED to
106 * indicate that the object is not operational. There are two exceptions from
107 * this rule:
108 * <ol>
109 * <li>If the @a aLimited argument is |true|, then this method will also
110 * succeeed if the object's state is Limited (or Ready, of course).</li>
111 * <li>If this method is called from the same thread that placed the object
112 * to InInit or InUninit state (i.e. either from within the AutoInitSpan
113 * or AutoUninitSpan scope), it will succeed as well (but will not
114 * increase the number of callers).</li>
115 * </ol>
116 *
117 * Normally, calling addCaller() never blocks. However, if this method is
118 * called by a thread created from within the AutoInitSpan scope and this
119 * scope is still active (i.e. the object state is InInit), it will block
120 * until the AutoInitSpan destructor signals that it has finished
121 * initialization.
122 *
123 * When this method returns a failure, the caller must not use the object
124 * and can return the failed result code to his caller.
125 *
126 * @param aState where to store the current object's state
127 * (can be used in overriden methods to determine the
128 * cause of the failure)
129 * @param aLimited |true| to add a limited caller.
130 * @return S_OK on success or E_UNEXPECTED on failure
131 *
132 * @note It is preferrable to use the #addLimitedCaller() rather than calling
133 * this method with @a aLimited = |true|, for better
134 * self-descriptiveness.
135 *
136 * @sa #addLimitedCaller()
137 * @sa #releaseCaller()
138 */
139HRESULT VirtualBoxBaseNEXT_base::addCaller (State *aState /* = NULL */,
140 bool aLimited /* = false */)
141{
142 AutoWriteLock stateLock (mStateLock);
143
144 HRESULT rc = E_UNEXPECTED;
145
146 if (mState == Ready || (aLimited && mState == Limited))
147 {
148 /* if Ready or allows Limited, increase the number of callers */
149 ++ mCallers;
150 rc = S_OK;
151 }
152 else
153 if ((mState == InInit || mState == InUninit))
154 {
155 if (mStateChangeThread == RTThreadSelf())
156 {
157 /*
158 * Called from the same thread that is doing AutoInitSpan or
159 * AutoUninitSpan, just succeed
160 */
161 rc = S_OK;
162 }
163 else if (mState == InInit)
164 {
165 /* addCaller() is called by a "child" thread while the "parent"
166 * thread is still doing AutoInitSpan/AutoReadySpan. Wait for the
167 * state to become either Ready/Limited or InitFailed/InInit/NotReady
168 * (in case of init failure). Note that we increase the number of
169 * callers anyway to prevent AutoUninitSpan from early completion.
170 */
171 ++ mCallers;
172
173 /* lazy creation */
174 if (mInitDoneSem == NIL_RTSEMEVENTMULTI)
175 RTSemEventMultiCreate (&mInitDoneSem);
176 ++ mInitDoneSemUsers;
177
178 LogFlowThisFunc (("Waiting for AutoInitSpan/AutoReadySpan to finish...\n"));
179
180 stateLock.leave();
181 RTSemEventMultiWait (mInitDoneSem, RT_INDEFINITE_WAIT);
182 stateLock.enter();
183
184 if (-- mInitDoneSemUsers == 0)
185 {
186 /* destroy the semaphore since no more necessary */
187 RTSemEventMultiDestroy (mInitDoneSem);
188 mInitDoneSem = NIL_RTSEMEVENTMULTI;
189 }
190
191 if (mState == Ready)
192 rc = S_OK;
193 else
194 {
195 AssertMsg (mCallers != 0, ("mCallers is ZERO!"));
196 -- mCallers;
197 if (mCallers == 0 && mState == InUninit)
198 {
199 /* inform AutoUninitSpan ctor there are no more callers */
200 RTSemEventSignal (mZeroCallersSem);
201 }
202 }
203 }
204 }
205
206 if (aState)
207 *aState = mState;
208
209 return rc;
210}
211
212/**
213 * Decrements the number of calls to this object by one.
214 * Must be called after every #addCaller() or #addLimitedCaller() when the
215 * object is no more necessary.
216 */
217void VirtualBoxBaseNEXT_base::releaseCaller()
218{
219 AutoWriteLock stateLock (mStateLock);
220
221 if (mState == Ready || mState == Limited)
222 {
223 /* if Ready or Limited, decrease the number of callers */
224 AssertMsgReturn (mCallers != 0, ("mCallers is ZERO!"), (void) 0);
225 -- mCallers;
226
227 return;
228 }
229
230 if ((mState == InInit || mState == InUninit))
231 {
232 if (mStateChangeThread == RTThreadSelf())
233 {
234 /*
235 * Called from the same thread that is doing AutoInitSpan or
236 * AutoUninitSpan, just succeed
237 */
238 return;
239 }
240
241 if (mState == InUninit)
242 {
243 /* the caller is being released after AutoUninitSpan has begun */
244 AssertMsgReturn (mCallers != 0, ("mCallers is ZERO!"), (void) 0);
245 -- mCallers;
246
247 if (mCallers == 0)
248 {
249 /* inform the AutoUninitSpan ctor there are no more callers */
250 RTSemEventSignal (mZeroCallersSem);
251 }
252
253 return;
254 }
255 }
256
257 AssertMsgFailed (("mState = %d!", mState));
258}
259
260// VirtualBoxBaseNEXT_base::AutoInitSpan methods
261////////////////////////////////////////////////////////////////////////////////
262
263/**
264 * Creates a smart initialization span object and places the object to
265 * InInit state.
266 *
267 * @param aObj |this| pointer of the managed VirtualBoxBase object whose
268 * init() method is being called
269 * @param aStatus initial initialization status for this span
270 */
271VirtualBoxBaseNEXT_base::AutoInitSpan::
272AutoInitSpan (VirtualBoxBaseNEXT_base *aObj, Status aStatus /* = Failed */)
273 : mObj (aObj), mStatus (aStatus), mOk (false)
274{
275 Assert (aObj);
276
277 AutoWriteLock stateLock (mObj->mStateLock);
278
279 Assert (mObj->mState != InInit && mObj->mState != InUninit &&
280 mObj->mState != InitFailed);
281
282 mOk = mObj->mState == NotReady;
283 if (!mOk)
284 return;
285
286 mObj->setState (InInit);
287}
288
289/**
290 * Places the managed VirtualBoxBase object to Ready/Limited state if the
291 * initialization succeeded or partly succeeded, or places it to InitFailed
292 * state and calls the object's uninit() method otherwise.
293 */
294VirtualBoxBaseNEXT_base::AutoInitSpan::~AutoInitSpan()
295{
296 /* if the state was other than NotReady, do nothing */
297 if (!mOk)
298 return;
299
300 AutoWriteLock stateLock (mObj->mStateLock);
301
302 Assert (mObj->mState == InInit);
303
304 if (mObj->mCallers > 0)
305 {
306 Assert (mObj->mInitDoneSemUsers > 0);
307
308 /* We have some pending addCaller() calls on other threads (created
309 * during InInit), signal that InInit is finished. */
310 RTSemEventMultiSignal (mObj->mInitDoneSem);
311 }
312
313 if (mStatus == Succeeded)
314 {
315 mObj->setState (Ready);
316 }
317 else
318 if (mStatus == Limited)
319 {
320 mObj->setState (VirtualBoxBaseNEXT_base::Limited);
321 }
322 else
323 {
324 mObj->setState (InitFailed);
325 /* leave the lock to prevent nesting when uninit() is called */
326 stateLock.leave();
327 /* call uninit() to let the object uninit itself after failed init() */
328 mObj->uninit();
329 /* Note: the object may no longer exist here (for example, it can call
330 * the destructor in uninit()) */
331 }
332}
333
334// VirtualBoxBaseNEXT_base::AutoReadySpan methods
335////////////////////////////////////////////////////////////////////////////////
336
337/**
338 * Creates a smart re-initialization span object and places the object to
339 * InInit state.
340 *
341 * @param aObj |this| pointer of the managed VirtualBoxBase object whose
342 * re-initialization method is being called
343 */
344VirtualBoxBaseNEXT_base::AutoReadySpan::
345AutoReadySpan (VirtualBoxBaseNEXT_base *aObj)
346 : mObj (aObj), mSucceeded (false), mOk (false)
347{
348 Assert (aObj);
349
350 AutoWriteLock stateLock (mObj->mStateLock);
351
352 Assert (mObj->mState != InInit && mObj->mState != InUninit &&
353 mObj->mState != InitFailed);
354
355 mOk = mObj->mState == Limited;
356 if (!mOk)
357 return;
358
359 mObj->setState (InInit);
360}
361
362/**
363 * Places the managed VirtualBoxBase object to Ready state if the
364 * re-initialization succeeded (i.e. #setSucceeded() has been called) or
365 * back to Limited state otherwise.
366 */
367VirtualBoxBaseNEXT_base::AutoReadySpan::~AutoReadySpan()
368{
369 /* if the state was other than Limited, do nothing */
370 if (!mOk)
371 return;
372
373 AutoWriteLock stateLock (mObj->mStateLock);
374
375 Assert (mObj->mState == InInit);
376
377 if (mObj->mCallers > 0 && mObj->mInitDoneSemUsers > 0)
378 {
379 /* We have some pending addCaller() calls on other threads,
380 * signal that InInit is finished. */
381 RTSemEventMultiSignal (mObj->mInitDoneSem);
382 }
383
384 if (mSucceeded)
385 {
386 mObj->setState (Ready);
387 }
388 else
389 {
390 mObj->setState (Limited);
391 }
392}
393
394// VirtualBoxBaseNEXT_base::AutoUninitSpan methods
395////////////////////////////////////////////////////////////////////////////////
396
397/**
398 * Creates a smart uninitialization span object and places this object to
399 * InUninit state.
400 *
401 * @note This method blocks the current thread execution until the number of
402 * callers of the managed VirtualBoxBase object drops to zero!
403 *
404 * @param aObj |this| pointer of the VirtualBoxBase object whose uninit()
405 * method is being called
406 */
407VirtualBoxBaseNEXT_base::AutoUninitSpan::AutoUninitSpan (VirtualBoxBaseNEXT_base *aObj)
408 : mObj (aObj), mInitFailed (false), mUninitDone (false)
409{
410 Assert (aObj);
411
412 AutoWriteLock stateLock (mObj->mStateLock);
413
414 Assert (mObj->mState != InInit);
415
416 /*
417 * Set mUninitDone to |true| if this object is already uninitialized
418 * (NotReady) or if another AutoUninitSpan is currently active on some
419 * other thread (InUninit).
420 */
421 mUninitDone = mObj->mState == NotReady ||
422 mObj->mState == InUninit;
423
424 if (mObj->mState == InitFailed)
425 {
426 /* we've been called by init() on failure */
427 mInitFailed = true;
428 }
429 else
430 {
431 /* do nothing if already uninitialized */
432 if (mUninitDone)
433 return;
434 }
435
436 /* go to InUninit to prevent from adding new callers */
437 mObj->setState (InUninit);
438
439 if (mObj->mCallers > 0)
440 {
441 /* lazy creation */
442 Assert (mObj->mZeroCallersSem == NIL_RTSEMEVENT);
443 RTSemEventCreate (&mObj->mZeroCallersSem);
444
445 /* wait until remaining callers release the object */
446 LogFlowThisFunc (("Waiting for callers (%d) to drop to zero...\n",
447 mObj->mCallers));
448
449 stateLock.leave();
450 RTSemEventWait (mObj->mZeroCallersSem, RT_INDEFINITE_WAIT);
451 }
452}
453
454/**
455 * Places the managed VirtualBoxBase object to the NotReady state.
456 */
457VirtualBoxBaseNEXT_base::AutoUninitSpan::~AutoUninitSpan()
458{
459 /* do nothing if already uninitialized */
460 if (mUninitDone)
461 return;
462
463 AutoWriteLock stateLock (mObj->mStateLock);
464
465 Assert (mObj->mState == InUninit);
466
467 mObj->setState (NotReady);
468}
469
470// VirtualBoxBase methods
471////////////////////////////////////////////////////////////////////////////////
472
473/**
474 * Translates the given text string according to the currently installed
475 * translation table and current context. The current context is determined
476 * by the context parameter. Additionally, a comment to the source text
477 * string text can be given. This comment (which is NULL by default)
478 * is helpful in sutuations where it is necessary to distinguish between
479 * two or more semantically different roles of the same source text in the
480 * same context.
481 *
482 * @param context the context of the the translation (can be NULL
483 * to indicate the global context)
484 * @param sourceText the string to translate
485 * @param comment the comment to the string (NULL means no comment)
486 *
487 * @return
488 * the translated version of the source string in UTF-8 encoding,
489 * or the source string itself if the translation is not found
490 * in the given context.
491 */
492// static
493const char *VirtualBoxBase::translate (const char *context, const char *sourceText,
494 const char *comment)
495{
496#if 0
497 Log(("VirtualBoxBase::translate:\n"
498 " context={%s}\n"
499 " sourceT={%s}\n"
500 " comment={%s}\n",
501 context, sourceText, comment));
502#endif
503
504 /// @todo (dmik) incorporate Qt translation file parsing and lookup
505
506 return sourceText;
507}
508
509// VirtualBoxSupportTranslationBase methods
510////////////////////////////////////////////////////////////////////////////////
511
512/**
513 * Modifies the given argument so that it will contain only a class name
514 * (null-terminated). The argument must point to a <b>non-constant</b>
515 * string containing a valid value, as it is generated by the
516 * __PRETTY_FUNCTION__ built-in macro of the GCC compiler, or by the
517 * __FUNCTION__ macro of any other compiler.
518 *
519 * The function assumes that the macro is used within the member of the
520 * class derived from the VirtualBoxSupportTranslation<> template.
521 *
522 * @param prettyFunctionName string to modify
523 * @return
524 * true on success and false otherwise
525 */
526bool VirtualBoxSupportTranslationBase::cutClassNameFrom__PRETTY_FUNCTION__ (char *fn)
527{
528 Assert (fn);
529 if (!fn)
530 return false;
531
532#if defined (__GNUC__)
533
534 // the format is like:
535 // VirtualBoxSupportTranslation<C>::VirtualBoxSupportTranslation() [with C = VirtualBox]
536
537 #define START " = "
538 #define END "]"
539
540#elif defined (_MSC_VER)
541
542 // the format is like:
543 // VirtualBoxSupportTranslation<class VirtualBox>::__ctor
544
545 #define START "<class "
546 #define END ">::"
547
548#endif
549
550 char *start = strstr (fn, START);
551 Assert (start);
552 if (start)
553 {
554 start += sizeof (START) - 1;
555 char *end = strstr (start, END);
556 Assert (end && (end > start));
557 if (end && (end > start))
558 {
559 size_t len = end - start;
560 memmove (fn, start, len);
561 fn [len] = 0;
562 return true;
563 }
564 }
565
566 #undef END
567 #undef START
568
569 return false;
570}
571
572// VirtualBoxSupportErrorInfoImplBase methods
573////////////////////////////////////////////////////////////////////////////////
574
575RTTLS VirtualBoxSupportErrorInfoImplBase::MultiResult::sCounter = NIL_RTTLS;
576
577void VirtualBoxSupportErrorInfoImplBase::MultiResult::init()
578{
579 if (sCounter == NIL_RTTLS)
580 {
581 sCounter = RTTlsAlloc();
582 AssertReturnVoid (sCounter != NIL_RTTLS);
583 }
584
585 uintptr_t counter = (uintptr_t) RTTlsGet (sCounter);
586 ++ counter;
587 RTTlsSet (sCounter, (void *) counter);
588}
589
590VirtualBoxSupportErrorInfoImplBase::MultiResult::~MultiResult()
591{
592 uintptr_t counter = (uintptr_t) RTTlsGet (sCounter);
593 AssertReturnVoid (counter != 0);
594 -- counter;
595 RTTlsSet (sCounter, (void *) counter);
596}
597
598/**
599 * Sets error info for the current thread. This is an internal function that
600 * gets eventually called by all public variants. If @a aWarning is
601 * @c true, then the highest (31) bit in the @a aResultCode value which
602 * indicates the error severity is reset to zero to make sure the receiver will
603 * recognize that the created error info object represents a warning rather
604 * than an error.
605 */
606/* static */
607HRESULT VirtualBoxSupportErrorInfoImplBase::setErrorInternal (
608 HRESULT aResultCode, const GUID &aIID,
609 const Bstr &aComponent, const Bstr &aText,
610 bool aWarning)
611{
612 /* whether multi-error mode is turned on */
613 bool preserve = ((uintptr_t) RTTlsGet (MultiResult::sCounter)) > 0;
614
615 LogRel (("ERROR [COM]: aRC=%#08x aIID={%Vuuid} aComponent={%ls} aText={%ls} "
616 "aWarning=%RTbool, preserve=%RTbool\n",
617 aResultCode, &aIID, aComponent.raw(), aText.raw(), aWarning,
618 preserve));
619
620 /* these are mandatory, others -- not */
621 AssertReturn ((!aWarning && FAILED (aResultCode)) ||
622 (aWarning && aResultCode != S_OK),
623 E_FAIL);
624 AssertReturn (!aText.isEmpty(), E_FAIL);
625
626 /* reset the error severity bit if it's a warning */
627 if (aWarning)
628 aResultCode &= ~0x80000000;
629
630 HRESULT rc = S_OK;
631
632 do
633 {
634 ComObjPtr <VirtualBoxErrorInfo> info;
635 rc = info.createObject();
636 CheckComRCBreakRC (rc);
637
638#if !defined (VBOX_WITH_XPCOM)
639
640 ComPtr <IVirtualBoxErrorInfo> curInfo;
641 if (preserve)
642 {
643 /* get the current error info if any */
644 ComPtr <IErrorInfo> err;
645 rc = ::GetErrorInfo (0, err.asOutParam());
646 CheckComRCBreakRC (rc);
647 rc = err.queryInterfaceTo (curInfo.asOutParam());
648 if (FAILED (rc))
649 {
650 /* create a IVirtualBoxErrorInfo wrapper for the native
651 * IErrorInfo object */
652 ComObjPtr <VirtualBoxErrorInfo> wrapper;
653 rc = wrapper.createObject();
654 if (SUCCEEDED (rc))
655 {
656 rc = wrapper->init (err);
657 if (SUCCEEDED (rc))
658 curInfo = wrapper;
659 }
660 }
661 }
662 /* On failure, curInfo will stay null */
663 Assert (SUCCEEDED (rc) || curInfo.isNull());
664
665 /* set the current error info and preserve the previous one if any */
666 rc = info->init (aResultCode, aIID, aComponent, aText, curInfo);
667 CheckComRCBreakRC (rc);
668
669 ComPtr <IErrorInfo> err;
670 rc = info.queryInterfaceTo (err.asOutParam());
671 if (SUCCEEDED (rc))
672 rc = ::SetErrorInfo (0, err);
673
674#else // !defined (VBOX_WITH_XPCOM)
675
676 nsCOMPtr <nsIExceptionService> es;
677 es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
678 if (NS_SUCCEEDED (rc))
679 {
680 nsCOMPtr <nsIExceptionManager> em;
681 rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
682 CheckComRCBreakRC (rc);
683
684 ComPtr <IVirtualBoxErrorInfo> curInfo;
685 if (preserve)
686 {
687 /* get the current error info if any */
688 ComPtr <nsIException> ex;
689 rc = em->GetCurrentException (ex.asOutParam());
690 CheckComRCBreakRC (rc);
691 rc = ex.queryInterfaceTo (curInfo.asOutParam());
692 if (FAILED (rc))
693 {
694 /* create a IVirtualBoxErrorInfo wrapper for the native
695 * nsIException object */
696 ComObjPtr <VirtualBoxErrorInfo> wrapper;
697 rc = wrapper.createObject();
698 if (SUCCEEDED (rc))
699 {
700 rc = wrapper->init (ex);
701 if (SUCCEEDED (rc))
702 curInfo = wrapper;
703 }
704 }
705 }
706 /* On failure, curInfo will stay null */
707 Assert (SUCCEEDED (rc) || curInfo.isNull());
708
709 /* set the current error info and preserve the previous one if any */
710 rc = info->init (aResultCode, aIID, aComponent, aText, curInfo);
711 CheckComRCBreakRC (rc);
712
713 ComPtr <nsIException> ex;
714 rc = info.queryInterfaceTo (ex.asOutParam());
715 if (SUCCEEDED (rc))
716 rc = em->SetCurrentException (ex);
717 }
718 else if (rc == NS_ERROR_UNEXPECTED)
719 {
720 /*
721 * It is possible that setError() is being called by the object
722 * after the XPCOM shutdown sequence has been initiated
723 * (for example, when XPCOM releases all instances it internally
724 * references, which can cause object's FinalConstruct() and then
725 * uninit()). In this case, do_GetService() above will return
726 * NS_ERROR_UNEXPECTED and it doesn't actually make sense to
727 * set the exception (nobody will be able to read it).
728 */
729 LogWarningFunc (("Will not set an exception because "
730 "nsIExceptionService is not available "
731 "(NS_ERROR_UNEXPECTED). "
732 "XPCOM is being shutdown?\n"));
733 rc = NS_OK;
734 }
735
736#endif // !defined (VBOX_WITH_XPCOM)
737 }
738 while (0);
739
740 AssertComRC (rc);
741
742 return SUCCEEDED (rc) ? aResultCode : rc;
743}
744
745// VirtualBoxBaseWithChildren methods
746////////////////////////////////////////////////////////////////////////////////
747
748/**
749 * Uninitializes all dependent children registered with #addDependentChild().
750 *
751 * @note
752 * This method will call uninit() methods of children. If these methods
753 * access the parent object, uninitDependentChildren() must be called
754 * either at the beginning of the parent uninitialization sequence (when
755 * it is still operational) or after setReady(false) is called to
756 * indicate the parent is out of action.
757 */
758void VirtualBoxBaseWithChildren::uninitDependentChildren()
759{
760 /// @todo (r=dmik) see todo in VirtualBoxBase.h, in
761 // template <class C> void removeDependentChild (C *child)
762
763 LogFlowThisFuncEnter();
764
765 AutoWriteLock alock (this);
766 AutoWriteLock mapLock (mMapLock);
767
768 LogFlowThisFunc (("count=%d...\n", mDependentChildren.size()));
769
770 if (mDependentChildren.size())
771 {
772 /* We keep the lock until we have enumerated all children.
773 * Those ones that will try to call #removeDependentChild() from
774 * a different thread will have to wait */
775
776 Assert (mUninitDoneSem == NIL_RTSEMEVENT);
777 int vrc = RTSemEventCreate (&mUninitDoneSem);
778 AssertRC (vrc);
779
780 Assert (mChildrenLeft == 0);
781 mChildrenLeft = (unsigned)mDependentChildren.size();
782
783 for (DependentChildren::iterator it = mDependentChildren.begin();
784 it != mDependentChildren.end(); ++ it)
785 {
786 VirtualBoxBase *child = (*it).second;
787 Assert (child);
788 if (child)
789 child->uninit();
790 }
791
792 mDependentChildren.clear();
793 }
794
795 /* Wait until all children started uninitializing on their own
796 * (and therefore are waiting for some parent's method or for
797 * #removeDependentChild() to return) are finished uninitialization */
798
799 if (mUninitDoneSem != NIL_RTSEMEVENT)
800 {
801 /* let stuck children run */
802 mapLock.leave();
803 alock.leave();
804
805 LogFlowThisFunc (("Waiting for uninitialization of all children...\n"));
806
807 RTSemEventWait (mUninitDoneSem, RT_INDEFINITE_WAIT);
808
809 alock.enter();
810 mapLock.enter();
811
812 RTSemEventDestroy (mUninitDoneSem);
813 mUninitDoneSem = NIL_RTSEMEVENT;
814 Assert (mChildrenLeft == 0);
815 }
816
817 LogFlowThisFuncLeave();
818}
819
820/**
821 * Returns a pointer to the dependent child corresponding to the given
822 * interface pointer (used as a key in the map) or NULL if the interface
823 * pointer doesn't correspond to any child registered using
824 * #addDependentChild().
825 *
826 * @param unk
827 * Pointer to map to the dependent child object (it is ComPtr <IUnknown>
828 * rather than IUnknown *, to guarantee IUnknown * identity)
829 * @return
830 * Pointer to the dependent child object
831 */
832VirtualBoxBase *VirtualBoxBaseWithChildren::getDependentChild (
833 const ComPtr <IUnknown> &unk)
834{
835 AssertReturn (!!unk, NULL);
836
837 AutoWriteLock alock (mMapLock);
838 if (mUninitDoneSem != NIL_RTSEMEVENT)
839 return NULL;
840
841 DependentChildren::const_iterator it = mDependentChildren.find (unk);
842 if (it == mDependentChildren.end())
843 return NULL;
844 return (*it).second;
845}
846
847/** Helper for addDependentChild() template method */
848void VirtualBoxBaseWithChildren::addDependentChild (
849 const ComPtr <IUnknown> &unk, VirtualBoxBase *child)
850{
851 AssertReturn (!!unk && child, (void) 0);
852
853 AutoWriteLock alock (mMapLock);
854
855 if (mUninitDoneSem != NIL_RTSEMEVENT)
856 {
857 // for this very unlikely case, we have to increase the number of
858 // children left, for symmetry with #removeDependentChild()
859 ++ mChildrenLeft;
860 return;
861 }
862
863 std::pair <DependentChildren::iterator, bool> result =
864 mDependentChildren.insert (DependentChildren::value_type (unk, child));
865 AssertMsg (result.second, ("Failed to insert a child to the map\n"));
866}
867
868/** Helper for removeDependentChild() template method */
869void VirtualBoxBaseWithChildren::removeDependentChild (const ComPtr <IUnknown> &unk)
870{
871 /// @todo (r=dmik) see todo in VirtualBoxBase.h, in
872 // template <class C> void removeDependentChild (C *child)
873
874 AssertReturn (!!unk, (void) 0);
875
876 AutoWriteLock alock (mMapLock);
877
878 if (mUninitDoneSem != NIL_RTSEMEVENT)
879 {
880 // uninitDependentChildren() is in action, just increase the number
881 // of children left and signal a semaphore when it reaches zero
882 Assert (mChildrenLeft != 0);
883 -- mChildrenLeft;
884 if (mChildrenLeft == 0)
885 {
886 int vrc = RTSemEventSignal (mUninitDoneSem);
887 AssertRC (vrc);
888 }
889 return;
890 }
891
892 DependentChildren::size_type result = mDependentChildren.erase (unk);
893 AssertMsg (result == 1, ("Failed to remove a child from the map\n"));
894 NOREF (result);
895}
896
897// VirtualBoxBaseWithChildrenNEXT methods
898////////////////////////////////////////////////////////////////////////////////
899
900/**
901 * Uninitializes all dependent children registered with #addDependentChild().
902 *
903 * Typically called from the uninit() method. Note that this method will call
904 * uninit() methods of child objects. If these methods need to call the parent
905 * object during initialization, uninitDependentChildren() must be called before
906 * the relevant part of the parent is uninitialized, usually at the begnning of
907 * the parent uninitialization sequence.
908 */
909void VirtualBoxBaseWithChildrenNEXT::uninitDependentChildren()
910{
911 LogFlowThisFuncEnter();
912
913 AutoWriteLock mapLock (mMapLock);
914
915 LogFlowThisFunc (("count=%u...\n", mDependentChildren.size()));
916
917 if (mDependentChildren.size())
918 {
919 /* We keep the lock until we have enumerated all children.
920 * Those ones that will try to call removeDependentChild() from a
921 * different thread will have to wait */
922
923 Assert (mUninitDoneSem == NIL_RTSEMEVENT);
924 int vrc = RTSemEventCreate (&mUninitDoneSem);
925 AssertRC (vrc);
926
927 Assert (mChildrenLeft == 0);
928 mChildrenLeft = mDependentChildren.size();
929
930 for (DependentChildren::iterator it = mDependentChildren.begin();
931 it != mDependentChildren.end(); ++ it)
932 {
933 VirtualBoxBase *child = (*it).second;
934 Assert (child);
935 if (child)
936 child->uninit();
937 }
938
939 mDependentChildren.clear();
940 }
941
942 /* Wait until all children that called uninit() on their own on other
943 * threads but stuck waiting for the map lock in removeDependentChild() have
944 * finished uninitialization. */
945
946 if (mUninitDoneSem != NIL_RTSEMEVENT)
947 {
948 /* let stuck children run */
949 mapLock.leave();
950
951 LogFlowThisFunc (("Waiting for uninitialization of all children...\n"));
952
953 RTSemEventWait (mUninitDoneSem, RT_INDEFINITE_WAIT);
954
955 mapLock.enter();
956
957 RTSemEventDestroy (mUninitDoneSem);
958 mUninitDoneSem = NIL_RTSEMEVENT;
959 Assert (mChildrenLeft == 0);
960 }
961
962 LogFlowThisFuncLeave();
963}
964
965/**
966 * Returns a pointer to the dependent child corresponding to the given
967 * interface pointer (used as a key in the map of dependent children) or NULL
968 * if the interface pointer doesn't correspond to any child registered using
969 * #addDependentChild().
970 *
971 * Note that ComPtr <IUnknown> is used as an argument instead of IUnknown * in
972 * order to guarantee IUnknown identity and disambiguation by doing
973 * QueryInterface (IUnknown) rather than a regular C cast.
974 *
975 * @param aUnk Pointer to map to the dependent child object.
976 * @return Pointer to the dependent child object.
977 */
978VirtualBoxBaseNEXT *
979VirtualBoxBaseWithChildrenNEXT::getDependentChild (const ComPtr <IUnknown> &aUnk)
980{
981 AssertReturn (!!aUnk, NULL);
982
983 AutoWriteLock alock (mMapLock);
984
985 /* return NULL if uninitDependentChildren() is in action */
986 if (mUninitDoneSem != NIL_RTSEMEVENT)
987 return NULL;
988
989 DependentChildren::const_iterator it = mDependentChildren.find (aUnk);
990 if (it == mDependentChildren.end())
991 return NULL;
992 return (*it).second;
993}
994
995void VirtualBoxBaseWithChildrenNEXT::doAddDependentChild (
996 IUnknown *aUnk, VirtualBoxBaseNEXT *aChild)
997{
998 AssertReturnVoid (aUnk && aChild);
999
1000 AutoWriteLock alock (mMapLock);
1001
1002 if (mUninitDoneSem != NIL_RTSEMEVENT)
1003 {
1004 /* uninitDependentChildren() is being run. For this very unlikely case,
1005 * we have to increase the number of children left, for symmetry with
1006 * a later #removeDependentChild() call. */
1007 ++ mChildrenLeft;
1008 return;
1009 }
1010
1011 std::pair <DependentChildren::iterator, bool> result =
1012 mDependentChildren.insert (DependentChildren::value_type (aUnk, aChild));
1013 AssertMsg (result.second, ("Failed to insert a child to the map\n"));
1014}
1015
1016void VirtualBoxBaseWithChildrenNEXT::doRemoveDependentChild (IUnknown *aUnk)
1017{
1018 AssertReturnVoid (aUnk);
1019
1020 AutoWriteLock alock (mMapLock);
1021
1022 if (mUninitDoneSem != NIL_RTSEMEVENT)
1023 {
1024 /* uninitDependentChildren() is being run. Just decrease the number of
1025 * children left and signal a semaphore if it reaches zero. */
1026 Assert (mChildrenLeft != 0);
1027 -- mChildrenLeft;
1028 if (mChildrenLeft == 0)
1029 {
1030 int vrc = RTSemEventSignal (mUninitDoneSem);
1031 AssertRC (vrc);
1032 }
1033 return;
1034 }
1035
1036 DependentChildren::size_type result = mDependentChildren.erase (aUnk);
1037 AssertMsg (result == 1, ("Failed to remove the child %p from the map\n",
1038 aUnk));
1039 NOREF (result);
1040}
1041
1042// VirtualBoxBaseWithTypedChildrenNEXT methods
1043////////////////////////////////////////////////////////////////////////////////
1044
1045/**
1046 * Uninitializes all dependent children registered with
1047 * #addDependentChild().
1048 *
1049 * @note This method will call uninit() methods of children. If these
1050 * methods access the parent object, uninitDependentChildren() must be
1051 * called either at the beginning of the parent uninitialization
1052 * sequence (when it is still operational) or after setReady(false) is
1053 * called to indicate the parent is out of action.
1054 */
1055template <class C>
1056void VirtualBoxBaseWithTypedChildrenNEXT <C>::uninitDependentChildren()
1057{
1058 AutoWriteLock mapLock (mMapLock);
1059
1060 if (mDependentChildren.size())
1061 {
1062 /* set flag to ignore #removeDependentChild() called from
1063 * child->uninit() */
1064 mInUninit = true;
1065
1066 /* leave the locks to let children waiting for
1067 * #removeDependentChild() run */
1068 mapLock.leave();
1069
1070 for (typename DependentChildren::iterator it = mDependentChildren.begin();
1071 it != mDependentChildren.end(); ++ it)
1072 {
1073 C *child = (*it);
1074 Assert (child);
1075 if (child)
1076 child->uninit();
1077 }
1078 mDependentChildren.clear();
1079
1080 mapLock.enter();
1081
1082 mInUninit = false;
1083 }
1084}
1085
1086// Settings API additions
1087////////////////////////////////////////////////////////////////////////////////
1088
1089#if defined VBOX_MAIN_SETTINGS_ADDONS
1090
1091namespace settings
1092{
1093
1094template<> stdx::char_auto_ptr
1095ToString <com::Bstr> (const com::Bstr &aValue, unsigned int aExtra)
1096{
1097 stdx::char_auto_ptr result;
1098
1099 if (aValue.raw() == NULL)
1100 throw ENoValue();
1101
1102 /* The only way to cause RTUtf16ToUtf8Ex return a number of bytes needed
1103 * w/o allocating the result buffer itself is to provide that both cch
1104 * and *ppsz are not NULL. */
1105 char dummy [1];
1106 char *dummy2 = dummy;
1107 size_t strLen = 1;
1108
1109 int vrc = RTUtf16ToUtf8Ex (aValue.raw(), RTSTR_MAX,
1110 &dummy2, strLen, &strLen);
1111 if (RT_SUCCESS (vrc))
1112 {
1113 /* the string only contains '\0' :) */
1114 result.reset (new char [1]);
1115 result.get() [0] = '\0';
1116 return result;
1117 }
1118
1119 if (vrc == VERR_BUFFER_OVERFLOW)
1120 {
1121 result.reset (new char [strLen + 1]);
1122 char *buf = result.get();
1123 vrc = RTUtf16ToUtf8Ex (aValue.raw(), RTSTR_MAX, &buf, strLen + 1, NULL);
1124 }
1125
1126 if (RT_FAILURE (vrc))
1127 throw LogicError (RT_SRC_POS);
1128
1129 return result;
1130}
1131
1132template<> com::Guid FromString <com::Guid> (const char *aValue)
1133{
1134 if (aValue == NULL)
1135 throw ENoValue();
1136
1137 /* For settings, the format is always {XXX...XXX} */
1138 char buf [RTUUID_STR_LENGTH];
1139 if (aValue == NULL || *aValue != '{' ||
1140 strlen (aValue) != RTUUID_STR_LENGTH + 1 ||
1141 aValue [RTUUID_STR_LENGTH] != '}')
1142 throw ENoConversion (FmtStr ("'%s' is not Guid", aValue));
1143
1144 /* strip { and } */
1145 memcpy (buf, aValue + 1, RTUUID_STR_LENGTH - 1);
1146 buf [RTUUID_STR_LENGTH - 1] = '\0';
1147 /* we don't use Guid (const char *) because we want to throw
1148 * ENoConversion on format error */
1149 RTUUID uuid;
1150 int vrc = RTUuidFromStr (&uuid, buf);
1151 if (RT_FAILURE (vrc))
1152 throw ENoConversion (FmtStr ("'%s' is not Guid (%Vrc)", aValue, vrc));
1153
1154 return com::Guid (uuid);
1155}
1156
1157template<> stdx::char_auto_ptr
1158ToString <com::Guid> (const com::Guid &aValue, unsigned int aExtra)
1159{
1160 /* For settings, the format is always {XXX...XXX} */
1161 stdx::char_auto_ptr result (new char [RTUUID_STR_LENGTH + 2]);
1162
1163 int vrc = RTUuidToStr (aValue.raw(), result.get() + 1, RTUUID_STR_LENGTH);
1164 if (RT_FAILURE (vrc))
1165 throw LogicError (RT_SRC_POS);
1166
1167 result.get() [0] = '{';
1168 result.get() [RTUUID_STR_LENGTH] = '}';
1169 result.get() [RTUUID_STR_LENGTH + 1] = '\0';
1170
1171 return result;
1172}
1173
1174} /* namespace settings */
1175
1176#endif /* VBOX_MAIN_SETTINGS_ADDONS */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette