VirtualBox

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

Last change on this file since 25809 was 25809, checked in by vboxsync, 15 years ago

Main: adjust lock validation code to use IPRT lock validation, first steps (not active yet)

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