VirtualBox

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

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

Added a setErrorNoLog method and aLogIt argument to the core setError methods (defaults to true) so that we can get rid of the disturbing release log errors caused by the USB drop down menu in the GUI.

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