VirtualBox

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

Last change on this file since 14772 was 14772, checked in by vboxsync, 16 years ago

Added vim modelines to aid following coding guidelines, like no tabs,
similar to what is already in the xidl file.

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