VirtualBoxBase.h revision 4bfa7b58e362a1bca0628643c352c137900bf01a
/** @file
*
* VBox frontends: Basic Frontend (BFE):
* Declarations of the BFE base classes
*/
/*
* Copyright (C) 2006-2007 Oracle Corporation
*
* 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 (GPL) 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.
*/
#ifndef ____H_VIRTUALBOXBASEIMPL
#define ____H_VIRTUALBOXBASEIMPL
#ifdef VBOXBFE_WITHOUT_COM
# include "COMDefs.h" // Our wrapper for COM definitions left in the code
# include <iprt/uuid.h>
#else
# include <VBox/com/defs.h>
#endif
#include <VBox/com/assert.h> // For the AssertComRC macro
#include <iprt/alloc.h>
#include <iprt/assert.h>
#include <iprt/critsect.h>
#include <iprt/string.h>
#include <iprt/asm.h> // for ASMReturnAddress
#include <list>
#include <map>
// defines
////////////////////////////////////////////////////////////////////////////////
#define VBOX_E_OBJECT_NOT_FOUND 0x80BB0001
#define VBOX_E_INVALID_VM_STATE 0x80BB0002
#define VBOX_E_VM_ERROR 0x80BB0003
#define VBOX_E_FILE_ERROR 0x80BB0004
#define VBOX_E_IPRT_ERROR 0x80BB0005
#define VBOX_E_PDM_ERROR 0x80BB0006
#define VBOX_E_INVALID_OBJECT_STATE 0x80BB0007
#define VBOX_E_HOST_ERROR 0x80BB0008
#define VBOX_E_NOT_SUPPORTED 0x80BB0009
#define VBOX_E_XML_ERROR 0x80BB000A
#define VBOX_E_INVALID_SESSION_STATE 0x80BB000B
#define VBOX_E_OBJECT_IN_USE 0x80BB000C
// macros and inlines
////////////////////////////////////////////////////////////////////////////////
/**
* A lightweight replacement for the COM setError function. I am
* assuming that this is only called in circumstances justifying
* an assertion.
*
* @returns error number
* @param iNum error number - this is simply returned
* @param pszFormat formatted error message
*/
static inline int setError(int iNum, const char *pszFormat, ...)
{
va_list args;
va_start(args, pszFormat);
AssertMsgFailed((pszFormat, args));
va_end(args);
return iNum;
}
/**
* Translate an error string. We do not do translation.
*/
#define tr(a) a
/**
* Assert macro wrapper.
*
* In the debug build, this macro is equivalent to Assert.
* In the release build, this is a no-op.
*
* @param expr Expression which should be true.
*/
#if defined (DEBUG)
#define ComAssert(expr) Assert (expr)
#else
#define ComAssert(expr) \
do { } while (0)
#endif
/**
* AssertMsg macro wrapper.
*
* In the debug build, this macro is equivalent to AssertMsg.
* In the release build, this is a no-op.
*
* 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 { } while (0)
#endif
/**
* AssertRC macro wrapper.
*
* In the debug build, this macro is equivalent to AssertRC.
* In the release build, this is a no-op.
*
* See ComAssert for more info.
*
* @param vrc VBox status code.
*/
#if defined (DEBUG)
#define ComAssertRC(vrc) AssertRC (vrc)
#else
#define ComAssertRC(vrc) ComAssertMsgRC (vrc, ("%Rra", vrc))
#endif
/**
* AssertMsgRC macro wrapper.
*
* In the debug build, this macro is equivalent to AssertMsgRC.
* In the release build, this is a no-op.
*
* 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 (RT_SUCCESS (vrc), msg)
#endif
/**
* AssertFailed macro wrapper.
*
* In the debug build, this macro is equivalent to AssertFailed.
* In the release build, this is a no-op.
*
* See ComAssert for more info.
*/
#if defined (DEBUG)
#define ComAssertFailed() AssertFailed()
#else
#define ComAssertFailed() \
do { } while (0)
#endif
/**
* AssertMsgFailed macro wrapper.
*
* In the debug build, this macro is equivalent to AssertMsgFailed.
* In the release build, this is a no-op.
*
* 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 { } while (0)
#endif
/**
* AssertComRC macro wrapper.
*
* In the debug build, this macro is equivalent to AssertComRC.
* In the release build, this is a no-op.
*
* 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 (!RT_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 (!RT_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 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 (!RT_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 (!RT_SUCCESS (vrc)) { eval; break; } } else do {} while (0)
/** Special version of ComAssertFailed that vaulates eval and breaks */
#define ComAssertFailedBreak(eval) \
if (1) { ComAssertFailed(); { eval; break; } } else do {} while (0)
/** Special version of ComAssertMsgFailed that vaulates eval and breaks */
#define ComAssertMsgFailedBreak(msg, eval) \
if (1) { ComAssertMsgFailed (msg); { eval; break; } } else do {} while (0)
/** Special version of ComAssertComRC that vaulates 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)
/**
* 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.
*/
#define CHECK_READY() \
do { \
if (!isReady()) \
return setError (E_UNEXPECTED, tr ("The object is not ready")); \
} while (0)
/**
* Declares an empty constructor 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 constructor and destructor for the given class.
* See DECLARE_EMPTY_CTOR_DTOR for more info.
*/
#define DEFINE_EMPTY_CTOR_DTOR(cls) \
cls::cls () {}; cls::~cls () {};
#define VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(cls, iface)
////////////////////////////////////////////////////////////////////////////////
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 VirtualBoxBase
{
public:
VirtualBoxBase()
{
mReady = false;
RTCritSectInit(&mCritSec);
}
virtual ~VirtualBoxBase()
{
RTCritSectDelete(&mCritSec);
}
/**
* Virtual uninitialization method. Called during parent object's
* uninitialization, if the given subclass instance is a dependent child of
* a class derived from VirtualBoxBaseWithChildren (@sa
* VirtualBoxBaseWithChildren::addDependentChild). In this case, this
* method's implementation must call setReady (false),
*/
virtual void uninit() {}
// lock the object
void lock()
{
RTCritSectEnter(&mCritSec);
}
// unlock the object
void unlock()
{
RTCritSectLeave(&mCritSec);
}
/** Returns true when the current thread owns this object's lock. */
bool isLockedOnCurrentThread()
{
return RTCritSectIsOwner (&mCritSec);
}
/**
* Helper class to make safe locking / unlocking.
* The constructor, given the VirtualBoxBase pointer, safely acquires the
* lock protecting its data. This lock will be released automatically
* when the instance goes out of scope (block, function etc.).
*
* @note
* An instance of this class must be declared as a local variable,
* otherwise the optimizer will most likely destruct it right after
* creation (but not at the end of the block), so the lock will be
* released immediately.
*/
class AutoLock
{
public:
#if defined(RT_STRICT)
# define ___CritSectEnter(cs) RTCritSectEnterDebug((cs), (RTUINTPTR)ASMReturnAddress(), "return address >>>", 0, __PRETTY_FUNCTION__)
#else
# define ___CritSectEnter(cs) RTCritSectEnter((cs))
#endif
/** Internal lock handle */
class Handle
{
public:
Handle (RTCRITSECT &critSect) : lock (critSect) {}
private:
RTCRITSECT &lock;
friend class AutoLock;
};
AutoLock() : mLock (NULL), mLevel (0), mLeftLevel (0) {}
AutoLock (VirtualBoxBase *that)
: mLock (that ? &that->mCritSec : NULL)
, mLevel (0), mLeftLevel (0)
{
if (mLock)
{
___CritSectEnter (mLock);
++ mLevel;
}
}
AutoLock (RTCRITSECT &critSect)
: mLock (&critSect), mLevel (0), mLeftLevel (0)
{
if (mLock)
{
___CritSectEnter (mLock);
++ mLevel;
}
}
AutoLock (const Handle &handle)
: mLock (&handle.lock), mLevel (0), mLeftLevel (0)
{
if (mLock)
{
___CritSectEnter (mLock);
++ mLevel;
}
}
~AutoLock()
{
if (mLock)
{
if (mLeftLevel)
{
mLeftLevel -= mLevel;
mLevel = 0;
for (; mLeftLevel; -- mLeftLevel)
RTCritSectEnter (mLock);
}
AssertMsg (mLevel <= 1, ("Lock level > 1: %d\n", mLevel));
for (; mLevel; -- mLevel)
RTCritSectLeave (mLock);
}
}
/**
* Tries to acquire the lock or increases the lock level
* if the lock is already owned by this thread.
*/
void lock()
{
if (mLock)
{
AssertMsgReturn (mLeftLevel == 0, ("lock() after leave()\n"), (void) 0);
___CritSectEnter (mLock);
++ mLevel;
}
}
/**
* Decreases the lock level. If the level goes to zero, the lock
* is released by the current thread.
*/
void unlock()
{
if (mLock)
{
AssertMsgReturn (mLevel > 0, ("Lock level is zero\n"), (void) 0);
AssertMsgReturn (mLeftLevel == 0, ("lock() after leave()\n"), (void) 0);
-- mLevel;
RTCritSectLeave (mLock);
}
}
/**
* Causes the current thread to completely release the lock
* (including locks acquired by all other instances of this class
* referring to the same object or handle). #enter() must be called
* to acquire the lock back and restore all lock levels.
*/
void leave()
{
if (mLock)
{
AssertMsg (mLevel > 0, ("Lock level is zero\n"));
AssertMsgReturn (mLeftLevel == 0, ("leave() w/o enter()\n"), (void) 0);
mLeftLevel = RTCritSectGetRecursion (mLock);
for (uint32_t left = mLeftLevel; left; -- left)
RTCritSectLeave (mLock);
Assert (mLeftLevel >= mLevel);
}
}
/**
* Causes the current thread to acquire the lock again and restore
* all lock levels after calling #leave().
*/
void enter()
{
if (mLock)
{
AssertMsg (mLevel > 0, ("Lock level is zero\n"));
AssertMsgReturn (mLeftLevel > 0, ("enter() w/o leave()\n"), (void) 0);
for (; mLeftLevel; -- mLeftLevel)
___CritSectEnter (mLock);
}
}
uint32_t level() const { return mLevel; }
bool isNull() const { return mLock == NULL; }
bool operator !() const { return isNull(); }
bool belongsTo (VirtualBoxBase *that) const
{
return that && &that->mCritSec == mLock;
}
private:
AutoLock (const AutoLock &that); // disabled
AutoLock &operator = (const AutoLock &that); // disabled
RTCRITSECT *mLock;
uint32_t mLevel;
uint32_t mLeftLevel;
#undef ___CritSectEnter
};
// sets the ready state of the object
void setReady(bool ready)
{
mReady = ready;
}
// get the ready state of the object
bool isReady()
{
return mReady;
}
/**
* Translates the given text string according to the currently installed
* translation table and current context. The current context is determined
* by the context parameter. Additionally, a comment to the source text
* string text can be given. This comment (which is NULL by default)
* is helpful in situations where it is necessary to distinguish between
* two or more semantically different roles of the same source text in the
* same context.
*
* @param context the context of the translation
* @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 given context.
*/
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
RTCRITSECT mCritSec;
};
////////////////////////////////////////////////////////////////////////////////
/**
* 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 *pData) {
AssertMsg (pData, ("new data must not be NULL"));
if (pData && mData != pData) {
if (mData && !mIsShared)
delete mData;
mData = pData;
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 *pData) {
AssertMsg (pData, ("new data must not be NULL"));
if (mData != pData) {
if (mData && !mIsShared)
delete mData;
mData = pData;
mIsShared = true;
}
}
void share (const Shareable &Data) { share (Data.mData); }
void attachCopy (const D *pData) {
AssertMsg (pData, ("data to copy must not be NULL"));
if (pData)
attach (new D (*pData));
}
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;
};
/**
* 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