VirtualBox

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

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

Main: backing out r57728 + r57730 which burn on windows

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