VirtualBoxBase.h revision 0010ccca43c2554000fdd0572c7b9cf5ad17ac91
/** @file
*
* VirtualBox COM base classes definition
*/
/*
* Copyright (C) 2006-2007 innotek GmbH
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation,
* in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
* distribution. VirtualBox OSE is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY of any kind.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*/
#ifndef ____H_VIRTUALBOXBASEIMPL
#define ____H_VIRTUALBOXBASEIMPL
#include "VBox/com/string.h"
#include "VBox/com/Guid.h"
#include "VBox/com/ptr.h"
#include "VBox/com/ErrorInfo.h"
#include "VBox/com/VirtualBox.h"
#include "AutoLock.h"
using namespace com;
using util::AutoLock;
using util::AutoReaderLock;
using util::AutoMultiLock;
#include <iprt/cdefs.h>
#include <iprt/critsect.h>
#include <list>
#include <map>
#if defined (RT_OS_WINDOWS)
#include <atlcom.h>
// use a special version of the singleton class factory,
// see KB811591 in msdn for more info.
#undef DECLARE_CLASSFACTORY_SINGLETON
#define DECLARE_CLASSFACTORY_SINGLETON(obj) DECLARE_CLASSFACTORY_EX(CMyComClassFactorySingleton<obj>)
template <class T>
class CMyComClassFactorySingleton : public CComClassFactory
{
public:
CMyComClassFactorySingleton() : m_hrCreate(S_OK){}
virtual ~CMyComClassFactorySingleton(){}
// IClassFactory
STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
{
HRESULT hRes = E_POINTER;
if (ppvObj != NULL)
{
*ppvObj = NULL;
// Aggregation is not supported in singleton objects.
ATLASSERT(pUnkOuter == NULL);
if (pUnkOuter != NULL)
hRes = CLASS_E_NOAGGREGATION;
else
{
if (m_hrCreate == S_OK && m_spObj == NULL)
{
Lock();
__try
{
// Fix: The following If statement was moved inside the __try statement.
// Did another thread arrive here first?
if (m_hrCreate == S_OK && m_spObj == NULL)
{
// lock the module to indicate activity
// (necessary for the monitor shutdown thread to correctly
// terminate the module in case when CreateInstance() fails)
_pAtlModule->Lock();
CComObjectCached<T> *p;
m_hrCreate = CComObjectCached<T>::CreateInstance(&p);
if (SUCCEEDED(m_hrCreate))
{
m_hrCreate = p->QueryInterface(IID_IUnknown, (void**)&m_spObj);
if (FAILED(m_hrCreate))
{
delete p;
}
}
_pAtlModule->Unlock();
}
}
__finally
{
Unlock();
}
}
if (m_hrCreate == S_OK)
{
hRes = m_spObj->QueryInterface(riid, ppvObj);
}
else
{
hRes = m_hrCreate;
}
}
}
return hRes;
}
HRESULT m_hrCreate;
CComPtr<IUnknown> m_spObj;
};
#endif // defined (RT_OS_WINDOWS)
// macros
////////////////////////////////////////////////////////////////////////////////
/**
* A special version of the Assert macro to be used within VirtualBoxBase
* subclasses that also inherit the VirtualBoxSupportErrorInfoImpl template.
*
* In the debug build, this macro is equivalent to Assert.
* In the release build, this macro uses |setError (E_FAIL, ...)| to set the
* error info from the asserted expression.
*
* @see VirtualBoxSupportErrorInfoImpl::setError
*
* @param expr Expression which should be true.
*/
#if defined (DEBUG)
#define ComAssert(expr) Assert (expr)
#else
#define ComAssert(expr) \
do { \
if (!(expr)) \
setError (E_FAIL, "Assertion failed: [%s] at '%s' (%d) in %s.\n" \
"Please contact the product vendor!", \
#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
} while (0)
#endif
/**
* A special version of the AssertMsg macro to be used within VirtualBoxBase
* subclasses that also inherit the VirtualBoxSupportErrorInfoImpl template.
*
* See ComAssert for more info.
*
* @param expr Expression which should be true.
* @param a printf argument list (in parenthesis).
*/
#if defined (DEBUG)
#define ComAssertMsg(expr, a) AssertMsg (expr, a)
#else
#define ComAssertMsg(expr, a) \
do { \
if (!(expr)) \
setError (E_FAIL, "Assertion failed: [%s] at '%s' (%d) in %s.\n" \
"%s.\n" \
"Please contact the product vendor!", \
#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__, Utf8StrFmt a .raw()); \
} while (0)
#endif
/**
* A special version of the AssertRC macro to be used within VirtualBoxBase
* subclasses that also inherit the VirtualBoxSupportErrorInfoImpl template.
*
* See ComAssert for more info.
*
* @param vrc VBox status code.
*/
#if defined (DEBUG)
#define ComAssertRC(vrc) AssertRC (vrc)
#else
#define ComAssertRC(vrc) ComAssertMsgRC (vrc, ("%Vra", vrc))
#endif
/**
* A special version of the AssertMsgRC macro to be used within VirtualBoxBase
* subclasses that also inherit the VirtualBoxSupportErrorInfoImpl template.
*
* See ComAssert for more info.
*
* @param vrc VBox status code.
* @param msg printf argument list (in parenthesis).
*/
#if defined (DEBUG)
#define ComAssertMsgRC(vrc, msg) AssertMsgRC (vrc, msg)
#else
#define ComAssertMsgRC(vrc, msg) ComAssertMsg (VBOX_SUCCESS (vrc), msg)
#endif
/**
* A special version of the AssertFailed macro to be used within VirtualBoxBase
* subclasses that also inherit the VirtualBoxSupportErrorInfoImpl template.
*
* See ComAssert for more info.
*/
#if defined (DEBUG)
#define ComAssertFailed() AssertFailed()
#else
#define ComAssertFailed() \
do { \
setError (E_FAIL, "Assertion failed at '%s' (%d) in %s.\n" \
"Please contact the product vendor!", \
__FILE__, __LINE__, __PRETTY_FUNCTION__); \
} while (0)
#endif
/**
* A special version of the AssertMsgFailed macro to be used within VirtualBoxBase
* subclasses that also inherit the VirtualBoxSupportErrorInfoImpl template.
*
* See ComAssert for more info.
*
* @param a printf argument list (in parenthesis).
*/
#if defined (DEBUG)
#define ComAssertMsgFailed(a) AssertMsgFailed(a)
#else
#define ComAssertMsgFailed(a) \
do { \
setError (E_FAIL, "Assertion failed at '%s' (%d) in %s.\n" \
"%s.\n" \
"Please contact the product vendor!", \
__FILE__, __LINE__, __PRETTY_FUNCTION__, Utf8StrFmt a .raw()); \
} while (0)
#endif
/**
* A special version of the AssertComRC macro to be used within VirtualBoxBase
* subclasses that also inherit the VirtualBoxSupportErrorInfoImpl template.
*
* See ComAssert for more info.
*
* @param rc COM result code
*/
#if defined (DEBUG)
#define ComAssertComRC(rc) AssertComRC (rc)
#else
#define ComAssertComRC(rc) ComAssertMsg (SUCCEEDED (rc), ("COM RC = 0x%08X\n", rc))
#endif
/** Special version of ComAssert that returns ret if expr fails */
#define ComAssertRet(expr, ret) \
do { ComAssert (expr); if (!(expr)) return (ret); } while (0)
/** Special version of ComAssertMsg that returns ret if expr fails */
#define ComAssertMsgRet(expr, a, ret) \
do { ComAssertMsg (expr, a); if (!(expr)) return (ret); } while (0)
/** Special version of ComAssertRC that returns ret if vrc does not succeed */
#define ComAssertRCRet(vrc, ret) \
do { ComAssertRC (vrc); if (!VBOX_SUCCESS (vrc)) return (ret); } while (0)
/** Special version of ComAssertMsgRC that returns ret if vrc does not succeed */
#define ComAssertMsgRCRet(vrc, msg, ret) \
do { ComAssertMsgRC (vrc, msg); if (!VBOX_SUCCESS (vrc)) return (ret); } while (0)
/** Special version of ComAssertFailed that returns ret */
#define ComAssertFailedRet(ret) \
do { ComAssertFailed(); return (ret); } while (0)
/** Special version of ComAssertMsgFailed that returns ret */
#define ComAssertMsgFailedRet(msg, ret) \
do { ComAssertMsgFailed (msg); return (ret); } while (0)
/** Special version of ComAssertComRC that returns ret if rc does not succeed */
#define ComAssertComRCRet(rc, ret) \
do { ComAssertComRC (rc); if (!SUCCEEDED (rc)) return (ret); } while (0)
/** Special version of ComAssertComRC that returns rc if rc does not succeed */
#define ComAssertComRCRetRC(rc) \
do { ComAssertComRC (rc); if (!SUCCEEDED (rc)) return (rc); } while (0)
/** Special version of ComAssert that evaulates eval and breaks if expr fails */
#define ComAssertBreak(expr, eval) \
if (1) { ComAssert (expr); if (!(expr)) { eval; break; } } else do {} while (0)
/** Special version of ComAssertMsg that evaulates eval and breaks if expr fails */
#define ComAssertMsgBreak(expr, a, eval) \
if (1) { ComAssertMsg (expr, a); if (!(expr)) { eval; break; } } else do {} while (0)
/** Special version of ComAssertRC that evaulates eval and breaks if vrc does not succeed */
#define ComAssertRCBreak(vrc, eval) \
if (1) { ComAssertRC (vrc); if (!VBOX_SUCCESS (vrc)) { eval; break; } } else do {} while (0)
/** Special version of ComAssertMsgRC that evaulates eval and breaks if vrc does not succeed */
#define ComAssertMsgRCBreak(vrc, msg, eval) \
if (1) { ComAssertMsgRC (vrc, msg); if (!VBOX_SUCCESS (vrc)) { eval; break; } } else do {} while (0)
/** Special version of ComAssertFailed that evaulates eval and breaks */
#define ComAssertFailedBreak(eval) \
if (1) { ComAssertFailed(); { eval; break; } } else do {} while (0)
/** Special version of ComAssertMsgFailed that evaulates eval and breaks */
#define ComAssertMsgFailedBreak(msg, eval) \
if (1) { ComAssertMsgFailed (msg); { eval; break; } } else do {} while (0)
/** Special version of ComAssertComRC that evaulates eval and breaks if rc does not succeed */
#define ComAssertComRCBreak(rc, eval) \
if (1) { ComAssertComRC (rc); if (!SUCCEEDED (rc)) { eval; break; } } else do {} while (0)
/** Special version of ComAssertComRC that just breaks if rc does not succeed */
#define ComAssertComRCBreakRC(rc) \
if (1) { ComAssertComRC (rc); if (!SUCCEEDED (rc)) { break; } } else do {} while (0)
/// @todo (dmik) remove after we switch to VirtualBoxBaseNEXT completely
/**
* Checks whether this object is ready or not. Objects are typically ready
* after they are successfully created by their parent objects and become
* not ready when the respective parent itsef becomes not ready or gets
* destroyed while a reference to the child is still held by the caller
* (which prevents it from destruction).
*
* When this object is not ready, the macro sets error info and returns
* E_UNEXPECTED (the translatable error message is defined in null context).
* Otherwise, the macro does nothing.
*
* This macro <b>must</b> be used at the beginning of all interface methods
* (right after entering the class lock) in classes derived from both
* VirtualBoxBase and VirtualBoxSupportErrorInfoImpl.
*/
#define CHECK_READY() \
do { \
if (!isReady()) \
return setError (E_UNEXPECTED, tr ("The object is not ready")); \
} while (0)
/**
* Declares an empty construtor and destructor for the given class.
* This is useful to prevent the compiler from generating the default
* ctor and dtor, which in turn allows to use forward class statements
* (instead of including their header files) when declaring data members of
* non-fundamental types with constructors (which are always called implicitly
* by constructors and by the destructor of the class).
*
* This macro is to be palced within (the public section of) the class
* declaration. Its counterpart, DEFINE_EMPTY_CTOR_DTOR, must be placed
* somewhere in one of the translation units (usually .cpp source files).
*
* @param cls class to declare a ctor and dtor for
*/
#define DECLARE_EMPTY_CTOR_DTOR(cls) cls(); ~cls();
/**
* Defines an empty construtor and destructor for the given class.
* See DECLARE_EMPTY_CTOR_DTOR for more info.
*/
#define DEFINE_EMPTY_CTOR_DTOR(cls) \
cls::cls () {}; cls::~cls () {};
////////////////////////////////////////////////////////////////////////////////
namespace stdx
{
/**
* A wrapper around the container that owns pointers it stores.
*
* @note
* Ownership is recognized only when destructing the container!
* Pointers are not deleted when erased using erase() etc.
*
* @param container
* class that meets Container requirements (for example, an instance of
* std::list<>, std::vector<> etc.). The given class must store
* pointers (for example, std::list <MyType *>).
*/
template <typename container>
class ptr_container : public container
{
public:
~ptr_container()
{
for (typename container::iterator it = container::begin();
it != container::end();
++ it)
delete (*it);
}
};
};
////////////////////////////////////////////////////////////////////////////////
class ATL_NO_VTABLE VirtualBoxBaseNEXT_base
#ifdef RT_OS_WINDOWS
: public CComObjectRootEx <CComMultiThreadModel>
#else
: public CComObjectRootEx
#endif
, public AutoLock::Lockable
{
public:
enum State { NotReady, Ready, InInit, InUninit, InitFailed, Limited };
protected:
VirtualBoxBaseNEXT_base();
virtual ~VirtualBoxBaseNEXT_base();
public:
// AutoLock::Lockable interface
virtual AutoLock::Handle *lockHandle() const;
/**
* Virtual unintialization method.
* Must be called by all implementations (COM classes) when the last
* reference to the object is released, before calling the destructor.
* Also, this method is called automatically by the uninit() method of the
* parent of this object, when this object is a dependent child of a class
* derived from VirtualBoxBaseWithChildren (@sa
* VirtualBoxBaseWithChildren::addDependentChild).
*/
virtual void uninit() {}
virtual HRESULT addCaller (State *aState = NULL, bool aLimited = false);
virtual void releaseCaller();
/**
* Adds a limited caller. This method is equivalent to doing
* <tt>addCaller (aState, true)</tt>, but it is preferred because
* provides better self-descriptiveness. See #addCaller() for more info.
*/
HRESULT addLimitedCaller (State *aState = NULL)
{
return addCaller (aState, true /* aLimited */);
}
/**
* Smart class that automatically increases the number of callers of the
* given VirtualBoxBase object when an instance is constructed and decreases
* it back when the created instance goes out of scope (i.e. gets destroyed).
*
* If #rc() returns a failure after the instance creation, it means that
* the managed VirtualBoxBase object is not Ready, or in any other invalid
* state, so that the caller must not use the object and can return this
* failed result code to the upper level.
*
* See VirtualBoxBase::addCaller(), VirtualBoxBase::addLimitedCaller() and
* VirtualBoxBase::releaseCaller() for more details about object callers.
*
* @param aLimited |false| if this template should use
* VirtualiBoxBase::addCaller() calls to add callers, or
* |true| if VirtualiBoxBase::addLimitedCaller() should be
* used.
*
* @note It is preferrable to use the AutoCaller and AutoLimitedCaller
* classes than specify the @a aLimited argument, for better
* self-descriptiveness.
*/
template <bool aLimited>
class AutoCallerBase
{
public:
/**
* Increases the number of callers of the given object
* by calling VirtualBoxBase::addCaller().
*
* @param aObj Object to add a caller to. If NULL, this
* instance is effectively turned to no-op (where
* rc() will return S_OK and state() will be
* NotReady).
*/
AutoCallerBase (VirtualBoxBaseNEXT_base *aObj)
: mObj (aObj)
, mRC (S_OK)
, mState (NotReady)
{
if (mObj)
mRC = mObj->addCaller (&mState, aLimited);
}
/**
* If the number of callers was successfully increased,
* decreases it using VirtualBoxBase::releaseCaller(), otherwise
* does nothing.
*/
~AutoCallerBase()
{
if (mObj && SUCCEEDED (mRC))
mObj->releaseCaller();
}
/**
* Stores the result code returned by VirtualBoxBase::addCaller()
* after instance creation or after the last #add() call. A successful
* result code means the number of callers was successfully increased.
*/
HRESULT rc() const { return mRC; }
/**
* Returns |true| if |SUCCEEDED (rc())| is |true|, for convenience.
* |true| means the number of callers was successfully increased.
*/
bool isOk() const { return SUCCEEDED (mRC); }
/**
* Stores the object state returned by VirtualBoxBase::addCaller()
* after instance creation or after the last #add() call.
*/
State state() const { return mState; }
/**
* Temporarily decreases the number of callers of the managed object.
* May only be called if #isOk() returns |true|. Note that #rc() will
* return E_FAIL after this method succeeds.
*/
void release()
{
Assert (SUCCEEDED (mRC));
if (SUCCEEDED (mRC))
{
if (mObj)
mObj->releaseCaller();
mRC = E_FAIL;
}
}
/**
* Restores the number of callers decreased by #release(). May only
* be called after #release().
*/
void add()
{
Assert (!SUCCEEDED (mRC));
if (mObj && !SUCCEEDED (mRC))
mRC = mObj->addCaller (&mState, aLimited);
}
private:
DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoCallerBase)
DECLARE_CLS_NEW_DELETE_NOOP (AutoCallerBase)
VirtualBoxBaseNEXT_base *mObj;
HRESULT mRC;
State mState;
};
/**
* Smart class that automatically increases the number of normal
* (non-limited) callers of the given VirtualBoxBase object when an
* instance is constructed and decreases it back when the created instance
* goes out of scope (i.e. gets destroyed).
*
* A typical usage pattern to declare a normal method of some object
* (i.e. a method that is valid only when the object provides its
* full functionality) is:
* <code>
* STDMETHODIMP Component::Foo()
* {
* AutoCaller autoCaller (this);
* CheckComRCReturnRC (autoCaller.rc());
* ...
* </code>
*
* Using this class is equivalent to using the AutoCallerBase template
* with the @a aLimited argument set to |false|, but this class is
* preferred because provides better self-descriptiveness.
*
* See AutoCallerBase for more information about auto caller functionality.
*/
typedef AutoCallerBase <false> AutoCaller;
/**
* Smart class that automatically increases the number of limited callers
* of the given VirtualBoxBase object when an instance is constructed and
* decreases it back when the created instance goes out of scope (i.e.
* gets destroyed).
*
* A typical usage pattern to declare a limited method of some object
* (i.e. a method that is valid even if the object doesn't provide its
* full functionality) is:
* <code>
* STDMETHODIMP Component::Bar()
* {
* AutoLimitedCaller autoCaller (this);
* CheckComRCReturnRC (autoCaller.rc());
* ...
* </code>
*
* Using this class is equivalent to using the AutoCallerBase template
* with the @a aLimited argument set to |true|, but this class is
* preferred because provides better self-descriptiveness.
*
* See AutoCallerBase for more information about auto caller functionality.
*/
typedef AutoCallerBase <true> AutoLimitedCaller;
protected:
/**
* Smart class to enclose the state transition NotReady->InInit->Ready.
*
* Instances must be created at the beginning of init() methods of
* VirtualBoxBase subclasses as a stack-based variable using |this| pointer
* as the argument. When this variable is created it automatically places
* the object to the InInit state.
*
* When the created variable goes out of scope (i.e. gets destroyed),
* depending on the success status of this initialization span, it either
* places the object to the Ready state or calls the object's
* VirtualBoxBase::uninit() method which is supposed to place the object
* back to the NotReady state using the AutoUninitSpan class.
*
* The initial success status of the initialization span is determined by
* the @a aSuccess argument of the AutoInitSpan constructor (|false| by
* default). Inside the initialization span, the success status can be set
* to |true| using #setSucceeded() or to |false| using #setFailed(). Please
* don't forget to set the correct success status before letting the
* AutoInitSpan variable go out of scope (for example, by performing an
* early return from the init() method)!
*
* Note that if an instance of this class gets constructed when the
* object is in the state other than NotReady, #isOk() returns |false| and
* methods of this class do nothing: the state transition is not performed.
*
* A typical usage pattern is:
* <code>
* HRESULT Component::init()
* {
* AutoInitSpan autoInitSpan (this);
* AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
* ...
* if (FAILED (rc))
* return rc;
* ...
* if (SUCCEEDED (rc))
* autoInitSpan.setSucceeded();
* return rc;
* }
* </code>
*
* @note Never create instances of this class outside init() methods of
* VirtualBoxBase subclasses and never pass anything other than |this| as
* the argument to the constructor!
*/
class AutoInitSpan
{
public:
enum Status { Failed = 0x0, Succeeded = 0x1, Limited = 0x2 };
AutoInitSpan (VirtualBoxBaseNEXT_base *aObj, Status aStatus = Failed);
~AutoInitSpan();
/**
* Returns |true| if this instance has been created at the right moment
* (when the object was in the NotReady state) and |false| otherwise.
*/
bool isOk() const { return mOk; }
/**
* Sets the initialization status to Succeeded to indicates successful
* initialization. The AutoInitSpan destructor will place the managed
* VirtualBoxBase object to the Ready state.
*/
void setSucceeded() { mStatus = Succeeded; }
/**
* Sets the initialization status to Succeeded to indicate limited
* (partly successful) initialization. The AutoInitSpan destructor will
* place the managed VirtualBoxBase object to the Limited state.
*/
void setLimited() { mStatus = Limited; }
/**
* Sets the initialization status to Failure to indicates failed
* initialization. The AutoInitSpan destructor will place the managed
* VirtualBoxBase object to the InitFailed state and will automatically
* call its uninit() method which is supposed to place the object back
* to the NotReady state using AutoUninitSpan.
*/
void setFailed() { mStatus = Failed; }
/** Returns the current initialization status. */
Status status() { return mStatus; }
private:
DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoInitSpan)
DECLARE_CLS_NEW_DELETE_NOOP (AutoInitSpan)
VirtualBoxBaseNEXT_base *mObj;
Status mStatus : 3; // must be at least total number of bits + 1 (sign)
bool mOk : 1;
};
/**
* Smart class to enclose the state transition Limited->InInit->Ready.
*
* Instances must be created at the beginning of methods of VirtualBoxBase
* subclasses that try to re-initialize the object to bring it to the
* Ready state (full functionality) after partial initialization
* (limited functionality)>, as a stack-based variable using |this| pointer
* as the argument. When this variable is created it automatically places
* the object to the InInit state.
*
* When the created variable goes out of scope (i.e. gets destroyed),
* depending on the success status of this initialization span, it either
* places the object to the Ready state or brings it back to the Limited
* state.
*
* The initial success status of the re-initialization span is |false|.
* In order to make it successful, #setSucceeded() must be called before
* the instance is destroyed.
*
* Note that if an instance of this class gets constructed when the
* object is in the state other than Limited, #isOk() returns |false| and
* methods of this class do nothing: the state transition is not performed.
*
* A typical usage pattern is:
* <code>
* HRESULT Component::reinit()
* {
* AutoReadySpan autoReadySpan (this);
* AssertReturn (autoReadySpan.isOk(), E_UNEXPECTED);
* ...
* if (FAILED (rc))
* return rc;
* ...
* if (SUCCEEDED (rc))
* autoReadySpan.setSucceeded();
* return rc;
* }
* </code>
*
* @note Never create instances of this class outside re-initialization
* methods of VirtualBoxBase subclasses and never pass anything other than
* |this| as the argument to the constructor!
*/
class AutoReadySpan
{
public:
AutoReadySpan (VirtualBoxBaseNEXT_base *aObj);
~AutoReadySpan();
/**
* Returns |true| if this instance has been created at the right moment
* (when the object was in the Limited state) and |false| otherwise.
*/
bool isOk() const { return mOk; }
/**
* Sets the re-initialization status to Succeeded to indicates
* successful re-initialization. The AutoReadySpan destructor will
* place the managed VirtualBoxBase object to the Ready state.
*/
void setSucceeded() { mSucceeded = true; }
private:
DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoReadySpan)
DECLARE_CLS_NEW_DELETE_NOOP (AutoReadySpan)
VirtualBoxBaseNEXT_base *mObj;
bool mSucceeded : 1;
bool mOk : 1;
};
/**
* Smart class to enclose the state transition Ready->InUnnit->NotReady or
* InitFailed->InUnnit->NotReady.
*
* Must be created at the beginning of uninit() methods of VirtualBoxBase
* subclasses as a stack-based variable using |this| pointer as the argument.
* When this variable is created it automatically places the object to the
* InUninit state, unless it is already in the NotReady state as indicated
* by #uninitDone() returning |true|. In the latter case, the uninit()
* method must immediately return because there should be nothing to
* uninitialize.
*
* When this variable goes out of scope (i.e. gets destroyed), it places
* the object to the NotReady state.
*
* A typical usage pattern is:
* <code>
* void Component::uninit()
* {
* AutoUninitSpan autoUninitSpan (this);
* if (autoUninitSpan.uninitDone())
* retrun;
* ...
* </code>
*
* @note Never create instances of this class outside uninit() methods and
* never pass anything other than |this| as the argument to the constructor!
*/
class AutoUninitSpan
{
public:
AutoUninitSpan (VirtualBoxBaseNEXT_base *aObj);
~AutoUninitSpan();
/** |true| when uninit() is called as a result of init() failure */
bool initFailed() { return mInitFailed; }
/** |true| when uninit() has already been called (so the object is NotReady) */
bool uninitDone() { return mUninitDone; }
private:
DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoUninitSpan)
DECLARE_CLS_NEW_DELETE_NOOP (AutoUninitSpan)
VirtualBoxBaseNEXT_base *mObj;
bool mInitFailed : 1;
bool mUninitDone : 1;
};
private:
void setState (State aState)
{
Assert (mState != aState);
mState = aState;
mStateChangeThread = RTThreadSelf();
}
/** Primary state of this object */
State mState;
/** Thread that caused the last state change */
RTTHREAD mStateChangeThread;
/** Total number of active calls to this object */
unsigned mCallers;
/** Semaphore posted when the number of callers drops to zero */
RTSEMEVENT mZeroCallersSem;
/** Semaphore posted when the object goes from InInit some other state */
RTSEMEVENTMULTI mInitDoneSem;
/** Number of threads waiting for mInitDoneSem */
unsigned mInitDoneSemUsers;
/** Protects access to state related data members */
RTCRITSECT mStateLock;
/** User-level object lock for subclasses */
mutable AutoLock::Handle *mObjectLock;
};
/**
* This macro adds the error info support to methods of the VirtualBoxBase
* class (by overriding them). Place it to the public section of the
* VirtualBoxBase subclass and the following methods will set the extended
* error info in case of failure instead of just returning the result code:
*
* <ul>
* <li>VirtualBoxBase::addCaller()
* </ul>
*
* @note The given VirtualBoxBase subclass must also inherit from both
* VirtualBoxSupportErrorInfoImpl and VirtualBoxSupportTranslation templates!
*
* @param C VirtualBoxBase subclass to add the error info support to
*/
#define VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(C) \
virtual HRESULT addCaller (VirtualBoxBaseNEXT_base::State *aState = NULL, \
bool aLimited = false) \
{ \
VirtualBoxBaseNEXT_base::State state; \
HRESULT rc = VirtualBoxBaseNEXT_base::addCaller (&state, aLimited); \
if (FAILED (rc)) \
{ \
if (state == VirtualBoxBaseNEXT_base::Limited) \
rc = setError (rc, tr ("The object functonality is limited")); \
else \
rc = setError (rc, tr ("The object is not ready")); \
} \
if (aState) \
*aState = state; \
return rc; \
} \
////////////////////////////////////////////////////////////////////////////////
/// @todo (dmik) remove after we switch to VirtualBoxBaseNEXT completely
class ATL_NO_VTABLE VirtualBoxBase : public VirtualBoxBaseNEXT_base
//#ifdef RT_OS_WINDOWS
// : public CComObjectRootEx<CComMultiThreadModel>
//#else
// : public CComObjectRootEx
//#endif
{
public:
VirtualBoxBase()
{
mReady = false;
}
virtual ~VirtualBoxBase()
{
}
/**
* Virtual unintialization method. Called during parent object's
* uninitialization, if the given subclass instance is a dependent child of
* a class dervived from VirtualBoxBaseWithChildren (@sa
* VirtualBoxBaseWithChildren::addDependentChild). In this case, this
* method's impelemtation must call setReady (false),
*/
virtual void uninit() {}
// sets the ready state of the object
void setReady(bool isReady)
{
mReady = isReady;
}
// get the ready state of the object
bool isReady()
{
return mReady;
}
static const char *translate (const char *context, const char *sourceText,
const char *comment = 0);
private:
// flag determining whether an object is ready
// for usage, i.e. methods may be called
bool mReady;
// mutex semaphore to lock the object
};
/**
* Temporary class to disable deprecated methods of VirtualBoxBase.
* Can be used as a base for components that are completely switched to
* the new locking scheme (VirtualBoxBaseNEXT_base).
*
* @todo remove after we switch to VirtualBoxBaseNEXT completely.
*/
class VirtualBoxBaseNEXT : public VirtualBoxBase
{
private:
void lock();
void unlock();
void setReady (bool isReady);
bool isReady();
};
////////////////////////////////////////////////////////////////////////////////
/** Helper for VirtualBoxSupportTranslation */
class VirtualBoxSupportTranslationBase
{
protected:
static bool cutClassNameFrom__PRETTY_FUNCTION__ (char *prettyFunctionName);
};
/**
* This template implements the NLS string translation support for the
* given class by providing a #tr() function.
*
* @param C class that needs to support the string translation
*
* @note
* Every class that wants to use the #tr() function in its own methods must
* inherit from this template, regardless of whether its base class (if any)
* inherits from it or not! Otherwise, the translation service will not
* work correctly. However, the declaration of the resulting class must
* contain the VIRTUALBOXSUPPORTTRANSLATION_OVERRIDE(<ClassName>) macro
* if one of its base classes also inherits from this template (to resolve
* the ambiguity of the #tr() function).
*/
template <class C>
class VirtualBoxSupportTranslation : virtual protected VirtualBoxSupportTranslationBase
{
public:
/**
* Translates the given text string according to the currently installed
* translation table and current context, which is determined by the
* class name. See VirtualBoxBase::translate() for more info.
*
* @param sourceText the string to translate
* @param comment the comment to the string (NULL means no comment)
*
* @return
* the translated version of the source string in UTF-8 encoding,
* or the source string itself if the translation is not found in
* the current context.
*/
inline static const char *tr (const char *sourceText, const char *comment = 0)
{
return VirtualBoxBase::translate (getClassName(), sourceText, comment);
}
protected:
static const char *getClassName()
{
static char fn [sizeof (__PRETTY_FUNCTION__) + 1];
if (!className)
{
strcpy (fn, __PRETTY_FUNCTION__);
cutClassNameFrom__PRETTY_FUNCTION__ (fn);
className = fn;
}
return className;
}
private:
static const char *className;
};
template <class C>
const char *VirtualBoxSupportTranslation <C>::className = NULL;
/**
* This macro must be invoked inside the public section of the declaration of
* the class inherited from the VirtualBoxSupportTranslation template, in case
* when one of its other base classes also inherits from that template. This is
* necessary to resolve the ambiguity of the #tr() function.
*
* @param C class that inherits from the VirtualBoxSupportTranslation template
* more than once (through its other base clases)
*/
#define VIRTUALBOXSUPPORTTRANSLATION_OVERRIDE(C) \
inline static const char *tr (const char *sourceText, const char *comment = 0) \
{ \
return VirtualBoxSupportTranslation <C>::tr (sourceText, comment); \
}
/**
* A dummy macro that is used to shut down Qt's lupdate tool warnings
* in some situations. This macro needs to be present inside (better at the
* very beginning) of the declaration of the class that inherits from
* VirtualBoxSupportTranslation template, to make lupdate happy.
*/
#define Q_OBJECT
////////////////////////////////////////////////////////////////////////////////
/**
* Helper for the VirtualBoxSupportErrorInfoImpl template.
*/
class VirtualBoxSupportErrorInfoImplBase
{
static HRESULT setErrorInternal (HRESULT aResultCode, const GUID &aIID,
const Bstr &aComponent, const Bstr &aText,
bool aPreserve);
protected:
inline static HRESULT setError (HRESULT aResultCode, const GUID &aIID,
const Bstr &aComponent,
const Bstr &aText)
{
return setErrorInternal (aResultCode, aIID, aComponent, aText,
false /* aPreserve */);
}
inline static HRESULT addError (HRESULT aResultCode, const GUID &aIID,
const Bstr &aComponent,
const Bstr &aText)
{
return setErrorInternal (aResultCode, aIID, aComponent, aText,
true /* aPreserve */);
}
static HRESULT setError (HRESULT aResultCode, const GUID &aIID,
const Bstr &aComponent,
const char *aText, va_list aArgs)
{
return setErrorInternal (aResultCode, aIID, aComponent,
Utf8StrFmt (aText, aArgs),
false /* aPreserve */);
}
static HRESULT addError (HRESULT aResultCode, const GUID &aIID,
const Bstr &aComponent,
const char *aText, va_list aArgs)
{
return setErrorInternal (aResultCode, aIID, aComponent,
Utf8StrFmt (aText, aArgs),
true /* aPreserve */);
}
};
/**
* This template implements ISupportErrorInfo for the given component class
* and provides the #setError() method to conveniently set the error information
* from within interface methods' implementations.
*
* On Windows, the template argument must define a COM interface map using
* BEGIN_COM_MAP / END_COM_MAP macros and this map must contain a
* COM_INTERFACE_ENTRY(ISupportErrorInfo) definition. All interface entries
* that follow it will be considered to support IErrorInfo, i.e. the
* InterfaceSupportsErrorInfo() implementation will return S_OK for the
* corresponding IID.
*
* On all platforms, the template argument must also define the following
* method: |public static const wchar_t *C::getComponentName()|. See
* #setError (HRESULT, const char *, ...) for a description on how it is
* used.
*
* @param C
* component class that implements one or more COM interfaces
* @param I
* default interface for the component. This interface's IID is used
* by the shortest form of #setError, for convenience.
*/
template <class C, class I>
class ATL_NO_VTABLE VirtualBoxSupportErrorInfoImpl
: protected VirtualBoxSupportErrorInfoImplBase
#if defined (RT_OS_WINDOWS)
, public ISupportErrorInfo
#else
#endif
{
public:
#if defined (RT_OS_WINDOWS)
STDMETHOD(InterfaceSupportsErrorInfo) (REFIID riid)
{
const _ATL_INTMAP_ENTRY* pEntries = C::_GetEntries();
Assert (pEntries);
if (!pEntries)
return S_FALSE;
BOOL bSupports = FALSE;
BOOL bISupportErrorInfoFound = FALSE;
while (pEntries->pFunc != NULL && !bSupports)
{
if (!bISupportErrorInfoFound)
{
// skip the com map entries until ISupportErrorInfo is found
bISupportErrorInfoFound =
InlineIsEqualGUID (*(pEntries->piid), IID_ISupportErrorInfo);
}
else
{
// look for the requested interface in the rest of the com map
bSupports = InlineIsEqualGUID (*(pEntries->piid), riid);
}
pEntries++;
}
Assert (bISupportErrorInfoFound);
return bSupports ? S_OK : S_FALSE;
}
#endif // defined (RT_OS_WINDOWS)
protected:
/**
* Sets the error information for the current thread.
* This information can be retrieved by a caller of an interface method
* using IErrorInfo on Windows or nsIException on Linux, or the cross-platform
* IVirtualBoxErrorInfo interface that provides extended error info (only
* for components from the VirtualBox COM library). Alternatively, the
* platform-independent class com::ErrorInfo (defined in VBox[XP]COM.lib)
* can be used to retrieve error info in a convenient way.
*
* It is assumed that the interface method that uses this function returns
* an unsuccessful result code to the caller (otherwise, there is no reason
* for the caller to try to retrieve error info after method invocation).
*
* Here is a table of correspondence between this method's arguments
* and IErrorInfo/nsIException/IVirtualBoxErrorInfo attributes/methods:
*
* argument IErrorInfo nsIException IVirtualBoxErrorInfo
* ----------------------------------------------------------------
* resultCode -- result resultCode
* iid GetGUID -- interfaceID
* component GetSource -- component
* text GetDescription message text
*
* This method is rarely needs to be used though. There are more convenient
* overloaded versions, that automatically substitute some arguments
* taking their values from the template parameters. See
* #setError (HRESULT, const char *, ...) for an example.
*
* @param aResultCode result (error) code, must not be S_OK
* @param aIID IID of the intrface that defines the error
* @param aComponent name of the component that generates the error
* @param aText error message (must not be null), an RTStrPrintf-like
* format string in UTF-8 encoding
* @param ... list of arguments for the format string
*
* @return
* the error argument, for convenience, If an error occures while
* creating error info itself, that error is returned instead of the
* error argument.
*/
inline static HRESULT setError (HRESULT aResultCode, const GUID &aIID,
const wchar_t *aComponent,
const char *aText, ...)
{
va_list args;
va_start (args, aText);
HRESULT rc = VirtualBoxSupportErrorInfoImplBase::setError
(aResultCode, aIID, aComponent, aText, args);
va_end (args);
return rc;
}
/**
* This method is the same as #setError() except that it preserves the
* error info object (if any) set for the current thread before this
* method is called by storing it in the IVirtualBoxErrorInfo::next
* attribute of the new error info object.
*/
inline static HRESULT addError (HRESULT aResultCode, const GUID &aIID,
const wchar_t *aComponent,
const char *aText, ...)
{
va_list args;
va_start (args, aText);
HRESULT rc = VirtualBoxSupportErrorInfoImplBase::addError
(aResultCode, aIID, aComponent, aText, args);
va_end (args);
return rc;
}
/**
* Sets the error information for the current thread.
* A convenience method that automatically sets the default interface
* ID (taken from the I template argument) and the component name
* (a value of C::getComponentName()).
*
* See #setError (HRESULT, const GUID &, const wchar_t *, const char *text, ...)
* for details.
*
* This method is the most common (and convenient) way to set error
* information from within interface methods. A typical pattern of usage
* is looks like this:
*
* <code>
* return setError (E_FAIL, "Terrible Error");
* </code>
* or
* <code>
* HRESULT rc = setError (E_FAIL, "Terrible Error");
* ...
* return rc;
* </code>
*/
inline static HRESULT setError (HRESULT aResultCode, const char *aText, ...)
{
va_list args;
va_start (args, aText);
HRESULT rc = VirtualBoxSupportErrorInfoImplBase::setError
(aResultCode, COM_IIDOF(I), C::getComponentName(), aText, args);
va_end (args);
return rc;
}
/**
* This method is the same as #setError() except that it preserves the
* error info object (if any) set for the current thread before this
* method is called by storing it in the IVirtualBoxErrorInfo::next
* attribute of the new error info object.
*/
inline static HRESULT addError (HRESULT aResultCode, const char *aText, ...)
{
va_list args;
va_start (args, aText);
HRESULT rc = VirtualBoxSupportErrorInfoImplBase::addError
(aResultCode, COM_IIDOF(I), C::getComponentName(), aText, args);
va_end (args);
return rc;
}
/**
* Sets the error information for the current thread, va_list variant.
* A convenience method that automatically sets the default interface
* ID (taken from the I template argument) and the component name
* (a value of C::getComponentName()).
*
* See #setError (HRESULT, const GUID &, const wchar_t *, const char *text, ...)
* and #setError (HRESULT, const char *, ...) for details.
*/
inline static HRESULT setErrorV (HRESULT aResultCode, const char *aText,
va_list aArgs)
{
HRESULT rc = VirtualBoxSupportErrorInfoImplBase::setError
(aResultCode, COM_IIDOF(I), C::getComponentName(), aText, aArgs);
return rc;
}
/**
* This method is the same as #setErrorV() except that it preserves the
* error info object (if any) set for the current thread before this
* method is called by storing it in the IVirtualBoxErrorInfo::next
* attribute of the new error info object.
*/
inline static HRESULT addErrorV (HRESULT aResultCode, const char *aText,
va_list aArgs)
{
HRESULT rc = VirtualBoxSupportErrorInfoImplBase::addError
(aResultCode, COM_IIDOF(I), C::getComponentName(), aText, aArgs);
return rc;
}
/**
* Sets the error information for the current thread, BStr variant.
* A convenience method that automatically sets the default interface
* ID (taken from the I template argument) and the component name
* (a value of C::getComponentName()).
*
* This method is preferred iy you have a ready (translated and formatted)
* Bstr string, because it omits an extra conversion Utf8Str -> Bstr.
*
* See #setError (HRESULT, const GUID &, const wchar_t *, const char *text, ...)
* and #setError (HRESULT, const char *, ...) for details.
*/
inline static HRESULT setErrorBstr (HRESULT aResultCode, const Bstr &aText)
{
HRESULT rc = VirtualBoxSupportErrorInfoImplBase::setError
(aResultCode, COM_IIDOF(I), C::getComponentName(), aText);
return rc;
}
/**
* This method is the same as #setErrorBstr() except that it preserves the
* error info object (if any) set for the current thread before this
* method is called by storing it in the IVirtualBoxErrorInfo::next
* attribute of the new error info object.
*/
inline static HRESULT addErrorBstr (HRESULT aResultCode, const Bstr &aText)
{
HRESULT rc = VirtualBoxSupportErrorInfoImplBase::addError
(aResultCode, COM_IIDOF(I), C::getComponentName(), aText);
return rc;
}
/**
* Sets the error information for the current thread.
* A convenience method that automatically sets the component name
* (a value of C::getComponentName()), but allows to specify the interface
* id manually.
*
* See #setError (HRESULT, const GUID &, const wchar_t *, const char *text, ...)
* for details.
*/
inline static HRESULT setError (HRESULT aResultCode, const GUID &aIID,
const char *aText, ...)
{
va_list args;
va_start (args, aText);
HRESULT rc = VirtualBoxSupportErrorInfoImplBase::setError
(aResultCode, aIID, C::getComponentName(), aText, args);
va_end (args);
return rc;
}
/**
* This method is the same as #setError() except that it preserves the
* error info object (if any) set for the current thread before this
* method is called by storing it in the IVirtualBoxErrorInfo::next
* attribute of the new error info object.
*/
inline static HRESULT addError (HRESULT aResultCode, const GUID &aIID,
const char *aText, ...)
{
va_list args;
va_start (args, aText);
HRESULT rc = VirtualBoxSupportErrorInfoImplBase::addError
(aResultCode, aIID, C::getComponentName(), aText, args);
va_end (args);
return rc;
}
private:
};
////////////////////////////////////////////////////////////////////////////////
/**
* Base class to track VirtualBoxBase chlidren of the component.
*
* This class is a preferrable VirtualBoxBase replacement for components
* that operate with collections of child components. It gives two useful
* possibilities:
*
* <ol><li>
* Given an IUnknown instance, it's possible to quickly determine
* whether this instance represents a child object created by the given
* component, and if so, get a valid VirtualBoxBase pointer to the child
* object. The returned pointer can be then safely casted to the
* actual class of the child object (to get access to its "internal"
* non-interface methods) provided that no other child components implement
* the same initial interface IUnknown is queried from.
* </li><li>
* When the parent object uninitializes itself, it can easily unintialize
* all its VirtualBoxBase derived children (using their
* VirtualBoxBase::uninit() implementations). This is done simply by
* calling the #uninitDependentChildren() method.
* </li><ol>
*
* In order to let the above work, the following must be done:
* <ol><li>
* When a child object is initialized, it calls #addDependentChild() of
* its parent to register itself within the list of dependent children.
* </li><li>
* When a child object it is uninitialized, it calls #removeDependentChild()
* to unregister itself. This must be done <b>after</b> the child has called
* setReady(false) to indicate it is no more valid, and <b>not</b> from under
* the child object's lock. Note also, that the first action the child's
* uninit() implementation must do is to check for readiness after acquiring
* the object's lock and return immediately if not ready.
* </li><ol>
*
* Children added by #addDependentChild() are <b>weakly</b> referenced
* (i.e. AddRef() is not called), so when a child is externally destructed
* (i.e. its reference count goes to zero), it will automatically remove
* itself from a map of dependent children, provided that it follows the
* rules described here.
*
* @note
* Because of weak referencing, deadlocks and assertions are very likely
* if #addDependentChild() or #removeDependentChild() are used incorrectly
* (called at inappropriate times). Check the above rules once more.
*/
class VirtualBoxBaseWithChildren : public VirtualBoxBase
{
public:
VirtualBoxBaseWithChildren()
: mUninitDoneSem (NIL_RTSEMEVENT), mChildrenLeft (0)
{
RTCritSectInit (&mMapLock);
}
virtual ~VirtualBoxBaseWithChildren()
{
RTCritSectDelete (&mMapLock);
}
/**
* Adds the given child to the map of dependent children.
* Intended to be called from the child's init() method,
* from under the child's lock.
*
* @param C the child object to add (must inherit VirtualBoxBase AND
* implement some interface)
*/
template <class C>
void addDependentChild (C *child)
{
AssertReturn (child, (void) 0);
addDependentChild (child, child);
}
/**
* Removes the given child from the map of dependent children.
* Must be called <b>after<b> the child has called setReady(false), and
* <b>not</b> from under the child object's lock.
*
* @param C the child object to remove (must inherit VirtualBoxBase AND
* implement some interface)
*/
template <class C>
void removeDependentChild (C *child)
{
AssertReturn (child, (void) 0);
/// @todo (r=dmik) the below check (and the relevant comment above)
// seems to be not necessary any more once we completely switch to
// the NEXT locking scheme. This requires altering removeDependentChild()
// and uninitDependentChildren() as well (due to the new state scheme,
// there is a separate mutex for state transition, so calling the
// child's uninit() from under the children map lock should not produce
// dead-locks any more).
Assert (!child->isLockedOnCurrentThread());
removeDependentChild (ComPtr <IUnknown> (child));
}
protected:
void uninitDependentChildren();
VirtualBoxBase *getDependentChild (const ComPtr <IUnknown> &unk);
private:
void addDependentChild (const ComPtr <IUnknown> &unk, VirtualBoxBase *child);
void removeDependentChild (const ComPtr <IUnknown> &unk);
typedef std::map <IUnknown *, VirtualBoxBase *> DependentChildren;
DependentChildren mDependentChildren;
RTCRITSECT mMapLock;
RTSEMEVENT mUninitDoneSem;
unsigned mChildrenLeft;
};
/**
* Temporary class to disable deprecated methods of VirtualBoxBase.
* Can be used as a base for components that are completely switched to
* the new locking scheme (VirtualBoxBaseNEXT_base).
*
* @todo remove after we switch to VirtualBoxBaseNEXT completely.
*/
class VirtualBoxBaseWithChildrenNEXT : public VirtualBoxBaseWithChildren
{
private:
void lock();
void unlock();
void setReady (bool isReady);
bool isReady();
};
////////////////////////////////////////////////////////////////////////////////
/**
* Base class to track component's chlidren of some particular type.
*
* This class is similar to VirtualBoxBaseWithChildren, with the exception
* that all children must be of the same type. For this reason, it's not
* necessary to use a map to store children, so a list is used instead.
*
* As opposed to VirtualBoxBaseWithChildren, children added by
* #addDependentChild() are <b>strongly</b> referenced, so that they cannot
* be externally destructed until #removeDependentChild() is called.
* For this reason, strict rules of calling #removeDependentChild() don't
* apply to instances of this class -- it can be called anywhere in the
* child's uninit() implementation.
*
* @param C type of child objects (must inherit VirtualBoxBase AND
* implement some interface)
*/
template <class C>
class VirtualBoxBaseWithTypedChildren : public VirtualBoxBase
{
public:
typedef std::list <ComObjPtr <C> > DependentChildren;
VirtualBoxBaseWithTypedChildren() : mInUninit (false) {}
virtual ~VirtualBoxBaseWithTypedChildren() {}
/**
* Adds the given child to the list of dependent children.
* Must be called from the child's init() method,
* from under the child's lock.
*
* @param C the child object to add (must inherit VirtualBoxBase AND
* implement some interface)
*/
void addDependentChild (C *child)
{
AssertReturn (child, (void) 0);
AutoLock alock (mMapLock);
if (mInUninit)
return;
mDependentChildren.push_back (child);
}
/**
* Removes the given child from the list of dependent children.
* Must be called from the child's uninit() method,
* under the child's lock.
*
* @param C the child object to remove (must inherit VirtualBoxBase AND
* implement some interface)
*/
void removeDependentChild (C *child)
{
AssertReturn (child, (void) 0);
AutoLock alock (mMapLock);
if (mInUninit)
return;
mDependentChildren.remove (child);
}
protected:
/**
* Returns an internal lock handle to lock the list of children
* returned by #dependentChildren() using AutoLock:
* <code>
* AutoLock alock (dependentChildrenLock());
* </code>
*/
AutoLock::Handle &dependentChildrenLock() const { return mMapLock; }
/**
* Returns the read-only list of all dependent children.
* @note
* Access the returned list (iterate, get size etc.) only after
* doing |AutoLock alock (dependentChildrenLock());|!
*/
const DependentChildren &dependentChildren() const { return mDependentChildren; }
/**
* Uninitializes all dependent children registered with #addDependentChild().
*
* @note
* This method will call uninit() methods of children. If these methods
* access the parent object, uninitDependentChildren() must be called
* either at the beginning of the parent uninitialization sequence (when
* it is still operational) or after setReady(false) is called to
* indicate the parent is out of action.
*/
void uninitDependentChildren()
{
AutoLock alock (this);
AutoLock mapLock (mMapLock);
if (mDependentChildren.size())
{
// set flag to ignore #removeDependentChild() called from child->uninit()
mInUninit = true;
// leave the locks to let children waiting for #removeDependentChild() run
mapLock.leave();
alock.leave();
for (typename DependentChildren::iterator it = mDependentChildren.begin();
it != mDependentChildren.end(); ++ it)
{
C *child = (*it);
Assert (child);
if (child)
child->uninit();
}
mDependentChildren.clear();
alock.enter();
mapLock.enter();
mInUninit = false;
}
}
/**
* Removes (detaches) all dependent children registered with
* #addDependentChild(), without uninitializing them.
*
* @note This method must be called from under the main object's lock
*/
void removeDependentChildren()
{
AutoLock alock (mMapLock);
mDependentChildren.clear();
}
private:
DependentChildren mDependentChildren;
bool mInUninit;
mutable AutoLock::Handle mMapLock;
};
/**
* Temporary class to disable deprecated methods of VirtualBoxBase.
* Can be used as a base for components that are completely switched to
* the new locking scheme (VirtualBoxBaseNEXT_base).
*
* @todo remove after we switch to VirtualBoxBaseNEXT completely.
*/
template <class C>
class VirtualBoxBaseWithTypedChildrenNEXT : public VirtualBoxBaseWithTypedChildren <C>
{
public:
typedef util::AutoLock AutoLock;
private:
void lock();
void unlock();
bool isLockedOnCurrentThread();
void setReady (bool isReady);
bool isReady();
};
////////////////////////////////////////////////////////////////////////////////
/// @todo (dmik) remove after we switch to VirtualBoxBaseNEXT completely
/**
* Simple template that manages data structure allocation/deallocation
* and supports data pointer sharing (the instance that shares the pointer is
* not responsible for memory deallocation as opposed to the instance that
* owns it).
*/
template <class D>
class Shareable
{
public:
Shareable() : mData (NULL), mIsShared (FALSE) {}
~Shareable() { free(); }
void allocate() { attach (new D); }
virtual void free() {
if (mData) {
if (!mIsShared)
delete mData;
mData = NULL;
mIsShared = false;
}
}
void attach (D *data) {
AssertMsg (data, ("new data must not be NULL"));
if (data && mData != data) {
if (mData && !mIsShared)
delete mData;
mData = data;
mIsShared = false;
}
}
void attach (Shareable &data) {
AssertMsg (
data.mData == mData || !data.mIsShared,
("new data must not be shared")
);
if (this != &data && !data.mIsShared) {
attach (data.mData);
data.mIsShared = true;
}
}
void share (D *data) {
AssertMsg (data, ("new data must not be NULL"));
if (mData != data) {
if (mData && !mIsShared)
delete mData;
mData = data;
mIsShared = true;
}
}
void share (const Shareable &data) { share (data.mData); }
void attachCopy (const D *data) {
AssertMsg (data, ("data to copy must not be NULL"));
if (data)
attach (new D (*data));
}
void attachCopy (const Shareable &data) {
attachCopy (data.mData);
}
virtual D *detach() {
D *d = mData;
mData = NULL;
mIsShared = false;
return d;
}
D *data() const {
return mData;
}
D *operator->() const {
AssertMsg (mData, ("data must not be NULL"));
return mData;
}
bool isNull() const { return mData == NULL; }
bool operator!() const { return isNull(); }
bool isShared() const { return mIsShared; }
protected:
D *mData;
bool mIsShared;
};
/// @todo (dmik) remove after we switch to VirtualBoxBaseNEXT completely
/**
* Simple template that enhances Shareable<> and supports data
* backup/rollback/commit (using the copy constructor of the managed data
* structure).
*/
template <class D>
class Backupable : public Shareable <D>
{
public:
Backupable() : Shareable <D> (), mBackupData (NULL) {}
void free()
{
AssertMsg (this->mData || !mBackupData, ("backup must be NULL if data is NULL"));
rollback();
Shareable <D>::free();
}
D *detach()
{
AssertMsg (this->mData || !mBackupData, ("backup must be NULL if data is NULL"));
rollback();
return Shareable <D>::detach();
}
void share (const Backupable &data)
{
AssertMsg (!data.isBackedUp(), ("data to share must not be backed up"));
if (!data.isBackedUp())
Shareable <D>::share (data.mData);
}
/**
* Stores the current data pointer in the backup area, allocates new data
* using the copy constructor on current data and makes new data active.
*/
void backup()
{
AssertMsg (this->mData, ("data must not be NULL"));
if (this->mData && !mBackupData)
{
mBackupData = this->mData;
this->mData = new D (*mBackupData);
}
}
/**
* Deletes new data created by #backup() and restores previous data pointer
* stored in the backup area, making it active again.
*/
void rollback()
{
if (this->mData && mBackupData)
{
delete this->mData;
this->mData = mBackupData;
mBackupData = NULL;
}
}
/**
* Commits current changes by deleting backed up data and clearing up the
* backup area. The new data pointer created by #backup() remains active
* and becomes the only managed pointer.
*
* This method is much faster than #commitCopy() (just a single pointer
* assignment operation), but makes the previous data pointer invalid
* (because it is freed). For this reason, this method must not be
* used if it's possible that data managed by this instance is shared with
* some other Shareable instance. See #commitCopy().
*/
void commit()
{
if (this->mData && mBackupData)
{
if (!this->mIsShared)
delete mBackupData;
mBackupData = NULL;
this->mIsShared = false;
}
}
/**
* Commits current changes by assigning new data to the previous data
* pointer stored in the backup area using the assignment operator.
* New data is deleted, the backup area is cleared and the previous data
* pointer becomes active and the only managed pointer.
*
* This method is slower than #commit(), but it keeps the previous data
* pointer valid (i.e. new data is copied to the same memory location).
* For that reason it's safe to use this method on instances that share
* managed data with other Shareable instances.
*/
void commitCopy()
{
if (this->mData && mBackupData)
{
*mBackupData = *(this->mData);
delete this->mData;
this->mData = mBackupData;
mBackupData = NULL;
}
}
void assignCopy (const D *data)
{
AssertMsg (this->mData, ("data must not be NULL"));
AssertMsg (data, ("data to copy must not be NULL"));
if (this->mData && data)
{
if (!mBackupData)
{
mBackupData = this->mData;
this->mData = new D (*data);
}
else
*this->mData = *data;
}
}
void assignCopy (const Backupable &data)
{
assignCopy (data.mData);
}
bool isBackedUp() const
{
return mBackupData != NULL;
}
bool hasActualChanges() const
{
AssertMsg (this->mData, ("data must not be NULL"));
return this->mData != NULL && mBackupData != NULL &&
!(*this->mData == *mBackupData);
}
D *backedUpData() const
{
return mBackupData;
}
protected:
D *mBackupData;
};
#endif // ____H_VIRTUALBOXBASEIMPL