VirtualBox

Changeset 51903 in vbox for trunk/src


Ignore:
Timestamp:
Jul 7, 2014 1:03:49 PM (11 years ago)
Author:
vboxsync
Message:

Main: AutoCaller/VirtualBoxBase refactoring, cleanly splitting out the object state handling, and moving all caller synchronization to one file. Also eliminated a not so vital template (AutoCallerBase) by much simpler inheritance. Theoretically has no visible effects, the behavior should be identical. Done as a preparation for reimplementing the caller synchronization.

Location:
trunk/src/VBox/Main
Files:
12 edited
2 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/Makefile.kmk

    r51476 r51903  
    346346        src-all/ProgressImpl.cpp \
    347347        src-all/SharedFolderImpl.cpp \
     348        src-all/AutoCaller.cpp \
    348349        src-all/VirtualBoxBase.cpp \
    349350        src-all/VirtualBoxErrorInfoImpl.cpp \
     
    694695        src-all/ProgressImpl.cpp \
    695696        src-all/SharedFolderImpl.cpp \
     697        src-all/AutoCaller.cpp \
    696698        src-all/VirtualBoxBase.cpp \
    697699        src-all/VirtualBoxErrorInfoImpl.cpp \
     
    815817        src-all/EventImpl.cpp \
    816818        src-all/Global.cpp \
     819        src-all/AutoCaller.cpp \
    817820        src-all/VirtualBoxBase.cpp \
    818821        src-all/VirtualBoxErrorInfoImpl.cpp \
  • trunk/src/VBox/Main/include/AutoCaller.h

    r44528 r51903  
    11/** @file
    22 *
    3  * VirtualBox COM base classes definition
     3 * VirtualBox object caller handling definitions
    44 */
    55
    66/*
    7  * Copyright (C) 2006-2012 Oracle Corporation
     7 * Copyright (C) 2006-2014 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    1919#define ____H_AUTOCALLER
    2020
     21#include "ObjectState.h"
     22
     23#include "VBox/com/AutoLock.h"
     24
     25// Forward declaration needed, but nothing more.
     26class VirtualBoxBase;
     27
     28
    2129////////////////////////////////////////////////////////////////////////////////
    2230//
     
    2533////////////////////////////////////////////////////////////////////////////////
    2634
     35
    2736/**
    28  * Smart class that automatically increases the number of callers of the
    29  * given VirtualBoxBase object when an instance is constructed and decreases
    30  * it back when the created instance goes out of scope (i.e. gets destroyed).
     37 * Smart class that automatically increases the number of normal (non-limited)
     38 * callers of the given VirtualBoxBase object when an instance is constructed
     39 * and decreases it back when the created instance goes out of scope (i.e. gets
     40 * destroyed).
    3141 *
    3242 * If #rc() returns a failure after the instance creation, it means that
     
    3545 * failed result code to the upper level.
    3646 *
    37  * See VirtualBoxBase::addCaller(), VirtualBoxBase::addLimitedCaller() and
    38  * VirtualBoxBase::releaseCaller() for more details about object callers.
    39  *
    40  * @param aLimited  |false| if this template should use
    41  *                  VirtualBoxBase::addCaller() calls to add callers, or
    42  *                  |true| if VirtualBoxBase::addLimitedCaller() should be
    43  *                  used.
    44  *
    45  * @note It is preferable to use the AutoCaller and AutoLimitedCaller
    46  *       classes than specify the @a aLimited argument, for better
    47  *       self-descriptiveness.
    48  */
    49 template<bool aLimited>
    50 class AutoCallerBase
    51 {
    52 public:
    53 
    54     /**
    55      * Increases the number of callers of the given object by calling
    56      * VirtualBoxBase::addCaller().
    57      *
    58      * @param aObj      Object to add a caller to. If NULL, this
    59      *                  instance is effectively turned to no-op (where
    60      *                  rc() will return S_OK and state() will be
    61      *                  NotReady).
    62      */
    63     AutoCallerBase(VirtualBoxBase *aObj)
    64         : mObj(aObj), mRC(S_OK), mState(VirtualBoxBase::NotReady)
    65     {
    66         if (mObj)
    67             mRC = mObj->addCaller(&mState, aLimited);
    68     }
    69 
    70     /**
    71      * If the number of callers was successfully increased, decreases it
    72      * using VirtualBoxBase::releaseCaller(), otherwise does nothing.
    73      */
    74     ~AutoCallerBase()
    75     {
    76         if (mObj && SUCCEEDED(mRC))
    77             mObj->releaseCaller();
    78     }
    79 
    80     /**
    81      * Stores the result code returned by VirtualBoxBase::addCaller() after
    82      * instance creation or after the last #add() call. A successful result
    83      * code means the number of callers was successfully increased.
    84      */
    85     HRESULT rc() const { return mRC; }
    86 
    87     /**
    88      * Returns |true| if |SUCCEEDED(rc())| is |true|, for convenience.
    89      * |true| means the number of callers was successfully increased.
    90      */
    91     bool isOk() const { return SUCCEEDED(mRC); }
    92 
    93     /**
    94      * Stores the object state returned by VirtualBoxBase::addCaller() after
    95      * instance creation or after the last #add() call.
    96      */
    97     VirtualBoxBase::State state() const { return mState; }
    98 
    99     /**
    100      * Temporarily decreases the number of callers of the managed object.
    101      * May only be called if #isOk() returns |true|. Note that #rc() will
    102      * return E_FAIL after this method succeeds.
    103      */
    104     void release()
    105     {
    106         Assert(SUCCEEDED(mRC));
    107         if (SUCCEEDED(mRC))
    108         {
    109             if (mObj)
    110                 mObj->releaseCaller();
    111             mRC = E_FAIL;
    112         }
    113     }
    114 
    115     /**
    116      * Restores the number of callers decreased by #release(). May only be
    117      * called after #release().
    118      */
    119     void add()
    120     {
    121         Assert(!SUCCEEDED(mRC));
    122         if (mObj && !SUCCEEDED(mRC))
    123             mRC = mObj->addCaller(&mState, aLimited);
    124     }
    125 
    126     /**
    127      * Attaches another object to this caller instance.
    128      * The previous object's caller is released before the new one is added.
    129      *
    130      * @param aObj  New object to attach, may be @c NULL.
    131      */
    132     void attach(VirtualBoxBase *aObj)
    133     {
    134         /* detect simple self-reattachment */
    135         if (mObj != aObj)
    136         {
    137             if (mObj && SUCCEEDED(mRC))
    138                 release();
    139             else if (!mObj)
    140             {
    141                 /* Fix up the success state when nothing is attached. Otherwise
    142                  * there are a couple of assertion which would trigger. */
    143                 mRC = E_FAIL;
    144             }
    145             mObj = aObj;
    146             add();
    147         }
    148     }
    149 
    150     /** Verbose equivalent to <tt>attach (NULL)</tt>. */
    151     void detach() { attach(NULL); }
    152 
    153 private:
    154 
    155     DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoCallerBase)
    156     DECLARE_CLS_NEW_DELETE_NOOP(AutoCallerBase)
    157 
    158     VirtualBoxBase *mObj;
    159     HRESULT mRC;
    160     VirtualBoxBase::State mState;
    161 };
    162 
    163 /**
    164  * Smart class that automatically increases the number of normal
    165  * (non-limited) callers of the given VirtualBoxBase object when an instance
    166  * is constructed and decreases it back when the created instance goes out
    167  * of scope (i.e. gets destroyed).
     47 * See ObjectState::addCaller() and ObjectState::releaseCaller() for more
     48 * details about object callers.
    16849 *
    16950 * A typical usage pattern to declare a normal method of some object (i.e. a
     
    18263 * }
    18364 * </code>
    184  *
    185  * Using this class is equivalent to using the AutoCallerBase template with
    186  * the @a aLimited argument set to |false|, but this class is preferred
    187  * because provides better self-descriptiveness.
    188  *
    189  * See AutoCallerBase for more information about auto caller functionality.
    190  */
    191 typedef AutoCallerBase<false> AutoCaller;
     65 */
     66class AutoCaller
     67{
     68public:
     69    /**
     70     * Default constructor. Not terribly useful, but it's valid to create
     71     * an instance without associating it with an object. It's a no-op,
     72     * like the more useful constructor below when NULL is passed to it.
     73     */
     74    AutoCaller()
     75    {
     76        init(NULL, false);
     77    }
     78
     79    /**
     80     * Increases the number of callers of the given object by calling
     81     * ObjectState::addCaller() for the corresponding member instance.
     82     *
     83     * @param aObj      Object to add a normal caller to. If NULL, this
     84     *                  instance is effectively turned to no-op (where
     85     *                  rc() will return S_OK).
     86     */
     87    AutoCaller(VirtualBoxBase *aObj)
     88    {
     89        init(aObj, false);
     90    }
     91
     92    /**
     93     * If the number of callers was successfully increased, decreases it
     94     * using ObjectState::releaseCaller(), otherwise does nothing.
     95     */
     96    ~AutoCaller()
     97    {
     98        if (mObj && SUCCEEDED(mRC))
     99            mObj->getObjectState().releaseCaller();
     100    }
     101
     102    /**
     103     * Returns the stored result code returned by ObjectState::addCaller()
     104     * after instance creation or after the last #add() call. A successful
     105     * result code means the number of callers was successfully increased.
     106     */
     107    HRESULT rc() const { return mRC; }
     108
     109    /**
     110     * Returns |true| if |SUCCEEDED(rc())| is |true|, for convenience.
     111     * |true| means the number of callers was successfully increased.
     112     */
     113    bool isOk() const { return SUCCEEDED(mRC); }
     114
     115    /**
     116     * Temporarily decreases the number of callers of the managed object.
     117     * May only be called if #isOk() returns |true|. Note that #rc() will
     118     * return E_FAIL after this method succeeds.
     119     */
     120    void release()
     121    {
     122        Assert(SUCCEEDED(mRC));
     123        if (SUCCEEDED(mRC))
     124        {
     125            if (mObj)
     126                mObj->getObjectState().releaseCaller();
     127            mRC = E_FAIL;
     128        }
     129    }
     130
     131    /**
     132     * Restores the number of callers decreased by #release(). May only be
     133     * called after #release().
     134     */
     135    void add()
     136    {
     137        Assert(!SUCCEEDED(mRC));
     138        if (mObj && !SUCCEEDED(mRC))
     139            mRC = mObj->getObjectState().addCaller(mLimited);
     140    }
     141
     142    /**
     143     * Attaches another object to this caller instance.
     144     * The previous object's caller is released before the new one is added.
     145     *
     146     * @param aObj  New object to attach, may be @c NULL.
     147     */
     148    void attach(VirtualBoxBase *aObj)
     149    {
     150        /* detect simple self-reattachment */
     151        if (mObj != aObj)
     152        {
     153            if (mObj && SUCCEEDED(mRC))
     154                release();
     155            else if (!mObj)
     156            {
     157                /* Fix up the success state when nothing is attached. Otherwise
     158                 * there are a couple of assertion which would trigger. */
     159                mRC = E_FAIL;
     160            }
     161            mObj = aObj;
     162            add();
     163        }
     164    }
     165
     166    /** Verbose equivalent to <tt>attach(NULL)</tt>. */
     167    void detach() { attach(NULL); }
     168
     169protected:
     170    /**
     171     * Internal constructor: Increases the number of callers of the given
     172     * object (either normal or limited variant) by calling
     173     * ObjectState::addCaller() for the corresponding member instance.
     174     *
     175     * @param aObj      Object to add a caller to. If NULL, this
     176     *                  instance is effectively turned to no-op (where
     177     *                  rc() will return S_OK).
     178     * @param aLimited  If |false|, then it's a regular caller, otherwise a
     179     *                  limited caller.
     180     */
     181    void init(VirtualBoxBase *aObj, bool aLimited)
     182    {
     183        mObj = aObj;
     184        mRC = S_OK;
     185        mLimited = aLimited;
     186        if (mObj)
     187            mRC = mObj->getObjectState().addCaller(mLimited);
     188    }
     189
     190private:
     191    DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoCaller)
     192    DECLARE_CLS_NEW_DELETE_NOOP(AutoCaller)
     193
     194    VirtualBoxBase *mObj;
     195    HRESULT mRC;
     196    bool mLimited;
     197};
    192198
    193199/**
     
    212218 * </code>
    213219 *
    214  * Using this class is equivalent to using the AutoCallerBase template with
    215  * the @a aLimited argument set to |true|, but this class is preferred
    216  * because provides better self-descriptiveness.
    217  *
    218  * See AutoCallerBase for more information about auto caller functionality.
    219  */
    220 typedef AutoCallerBase<true> AutoLimitedCaller;
     220 * See AutoCaller for more information about auto caller functionality.
     221 */
     222class AutoLimitedCaller : public AutoCaller
     223{
     224public:
     225    /**
     226     * Default constructor. Not terribly useful, but it's valid to create
     227     * an instance without associating it with an object. It's a no-op,
     228     * like the more useful constructor below when NULL is passed to it.
     229     */
     230    AutoLimitedCaller()
     231    {
     232        AutoCaller::init(NULL, true);
     233    }
     234
     235    /**
     236     * Increases the number of callers of the given object by calling
     237     * ObjectState::addCaller() for the corresponding member instance.
     238     *
     239     * @param aObj      Object to add a limited caller to. If NULL, this
     240     *                  instance is effectively turned to no-op (where
     241     *                  rc() will return S_OK).
     242     */
     243    AutoLimitedCaller(VirtualBoxBase *aObj)
     244    {
     245        AutoCaller::init(aObj, true);
     246    }
     247
     248};
    221249
    222250/**
     
    349377 * HRESULT Component::reinit()
    350378 * {
    351  *     AutoReinitSpan autoReinitSpan (this);
    352  *     AssertReturn (autoReinitSpan.isOk(), E_FAIL);
     379 *     AutoReinitSpan autoReinitSpan(this);
     380 *     AssertReturn(autoReinitSpan.isOk(), E_FAIL);
    353381 *     ...
    354382 *     if (FAILED(rc))
     
    416444 * void Component::uninit()
    417445 * {
    418  *     AutoUninitSpan autoUninitSpan (this);
     446 *     AutoUninitSpan autoUninitSpan(this);
    419447 *     if (autoUninitSpan.uninitDone())
    420448 *         return;
     
    424452 *
    425453 * @note The constructor of this class blocks the current thread execution
    426  *       until the number of callers added to the object using #addCaller()
    427  *       or AutoCaller drops to zero. For this reason, it is forbidden to
    428  *       create instances of this class (or call uninit()) within the
    429  *       AutoCaller or #addCaller() scope because it is a guaranteed
    430  *       deadlock.
     454 *       until the number of callers added to the object using
     455 *       ObjectState::addCaller() or AutoCaller drops to zero. For this reason,
     456 *       it is forbidden to create instances of this class (or call uninit())
     457 *       within the AutoCaller or ObjectState::addCaller() scope because it is
     458 *       a guaranteed deadlock.
    431459 *
    432460 * @note Never create instances of this class outside uninit() methods and
  • trunk/src/VBox/Main/include/ObjectState.h

    r51732 r51903  
    11/** @file
    22 *
    3  * VirtualBox COM base classes definition
     3 * VirtualBox object state handling definitions
    44 */
    55
    66/*
    7  * Copyright (C) 2006-2012 Oracle Corporation
     7 * Copyright (C) 2006-2014 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    1616 */
    1717
    18 #ifndef ____H_AUTOCALLER
    19 #define ____H_AUTOCALLER
     18#ifndef ____H_OBJECTSTATE
     19#define ____H_OBJECTSTATE
     20
     21#include "VBox/com/defs.h"
     22#include "VBox/com/AutoLock.h"
     23
     24// Forward declaration needed, but nothing more.
     25class VirtualBoxBase;
    2026
    2127////////////////////////////////////////////////////////////////////////////////
    2228//
    23 // AutoCaller* classes
     29// ObjectState
    2430//
    2531////////////////////////////////////////////////////////////////////////////////
    2632
    2733/**
    28  * Smart class that automatically increases the number of callers of the
    29  * given VirtualBoxBase object when an instance is constructed and decreases
    30  * it back when the created instance goes out of scope (i.e. gets destroyed).
     34 * Thec functionality implemented by this class is the primary object state
     35 * (used by VirtualBoxBase and thus part of all API classes) that indicates
     36 * if the object is ready to serve the calls, and if not, what stage it is
     37 * currently at. Here is the primary state diagram:
    3138 *
    32  * If #rc() returns a failure after the instance creation, it means that
    33  * the managed VirtualBoxBase object is not Ready, or in any other invalid
    34  * state, so that the caller must not use the object and can return this
    35  * failed result code to the upper level.
     39 *              +-------------------------------------------------------+
     40 *              |                                                       |
     41 *              |         (InitFailed) -----------------------+         |
     42 *              |              ^                              |         |
     43 *              v              |                              v         |
     44 *  [*] ---> NotReady ----> (InInit) -----> Ready -----> (InUninit) ----+
     45 *                     ^       |
     46 *                     |       v
     47 *                     |    Limited
     48 *                     |       |
     49 *                     +-------+
    3650 *
    37  * See VirtualBoxBase::addCaller(), VirtualBoxBase::addLimitedCaller() and
    38  * VirtualBoxBase::releaseCaller() for more details about object callers.
     51 * The object is fully operational only when its state is Ready. The Limited
     52 * state means that only some vital part of the object is operational, and it
     53 * requires some sort of reinitialization to become fully operational. The
     54 * NotReady state means the object is basically dead: it either was not yet
     55 * initialized after creation at all, or was uninitialized and is waiting to be
     56 * destroyed when the last reference to it is released. All other states are
     57 * transitional.
    3958 *
    40  * @param aLimited  |false| if this template should use
    41  *                  VirtualBoxBase::addCaller() calls to add callers, or
    42  *                  |true| if VirtualBoxBase::addLimitedCaller() should be
    43  *                  used.
     59 * The NotReady->InInit->Ready, NotReady->InInit->Limited and
     60 * NotReady->InInit->InitFailed transition is done by the AutoInitSpan smart
     61 * class.
    4462 *
    45  * @note It is preferable to use the AutoCaller and AutoLimitedCaller
    46  *       classes than specify the @a aLimited argument, for better
    47  *       self-descriptiveness.
     63 * The Limited->InInit->Ready, Limited->InInit->Limited and
     64 * Limited->InInit->InitFailed transition is done by the AutoReinitSpan smart
     65 * class.
     66 *
     67 * The Ready->InUninit->NotReady and InitFailed->InUninit->NotReady
     68 * transitions are done by the AutoUninitSpan smart class.
     69 *
     70 * In order to maintain the primary state integrity and declared functionality
     71 * the following rules apply everywhere:
     72 *
     73 * 1) Use the above Auto*Span classes to perform state transitions. See the
     74 *    individual class descriptions for details.
     75 *
     76 * 2) All public methods of subclasses (i.e. all methods that can be called
     77 *    directly, not only from within other methods of the subclass) must have a
     78 *    standard prolog as described in the AutoCaller and AutoLimitedCaller
     79 *    documentation. Alternatively, they must use #addCaller() and
     80 *    #releaseCaller() directly (and therefore have both the prolog and the
     81 *    epilog), but this is not recommended because it is easy to forget the
     82 *    matching release, e.g. returning before reaching the call.
    4883 */
    49 template<bool aLimited>
    50 class AutoCallerBase
     84class ObjectState
    5185{
    5286public:
     87    enum State { NotReady, Ready, InInit, InUninit, InitFailed, Limited };
    5388
    54     /**
    55      * Increases the number of callers of the given object by calling
    56      * VirtualBoxBase::addCaller().
    57      *
    58      * @param aObj      Object to add a caller to. If NULL, this
    59      *                  instance is effectively turned to no-op (where
    60      *                  rc() will return S_OK and state() will be
    61      *                  NotReady).
    62      */
    63     AutoCallerBase(VirtualBoxBase *aObj)
    64         : mObj(aObj), mRC(S_OK), mState(VirtualBoxBase::NotReady)
    65     {
    66         if (mObj)
    67             mRC = mObj->addCaller(&mState, aLimited);
    68     }
     89    ObjectState(VirtualBoxBase *aObj);
     90    ~ObjectState();
    6991
    70     /**
    71      * If the number of callers was successfully increased, decreases it
    72      * using VirtualBoxBase::releaseCaller(), otherwise does nothing.
    73      */
    74     ~AutoCallerBase()
    75     {
    76         if (mObj && SUCCEEDED(mRC))
    77             mObj->releaseCaller();
    78     }
     92    State getState();
    7993
    80     /**
    81      * Stores the result code returned by VirtualBoxBase::addCaller() after
    82      * instance creation or after the last #add() call. A successful result
    83      * code means the number of callers was successfully increased.
    84      */
    85     HRESULT rc() const { return mRC; }
     94    HRESULT addCaller(bool aLimited = false);
     95    void releaseCaller();
    8696
    87     /**
    88      * Returns |true| if |SUCCEEDED(rc())| is |true|, for convenience.
    89      * |true| means the number of callers was successfully increased.
    90      */
    91     bool isOk() const { return SUCCEEDED(mRC); }
    92 
    93     /**
    94      * Stores the object state returned by VirtualBoxBase::addCaller() after
    95      * instance creation or after the last #add() call.
    96      */
    97     VirtualBoxBase::State state() const { return mState; }
    98 
    99     /**
    100      * Temporarily decreases the number of callers of the managed object.
    101      * May only be called if #isOk() returns |true|. Note that #rc() will
    102      * return E_FAIL after this method succeeds.
    103      */
    104     void release()
    105     {
    106         Assert(SUCCEEDED(mRC));
    107         if (SUCCEEDED(mRC))
    108         {
    109             if (mObj)
    110                 mObj->releaseCaller();
    111             mRC = E_FAIL;
    112         }
    113     }
    114 
    115     /**
    116      * Restores the number of callers decreased by #release(). May only be
    117      * called after #release().
    118      */
    119     void add()
    120     {
    121         Assert(!SUCCEEDED(mRC));
    122         if (mObj && !SUCCEEDED(mRC))
    123             mRC = mObj->addCaller(&mState, aLimited);
    124     }
    125 
    126     /**
    127      * Attaches another object to this caller instance.
    128      * The previous object's caller is released before the new one is added.
    129      *
    130      * @param aObj  New object to attach, may be @c NULL.
    131      */
    132     void attach(VirtualBoxBase *aObj)
    133     {
    134         /* detect simple self-reattachment */
    135         if (mObj != aObj)
    136         {
    137             if (mObj && SUCCEEDED(mRC))
    138                 release();
    139             else if (!mObj)
    140             {
    141                 /* Fix up the success state when nothing is attached. Otherwise
    142                  * there are a couple of assertion which would trigger. */
    143                 mRC = E_FAIL;
    144             }
    145             mObj = aObj;
    146             add();
    147         }
    148     }
    149 
    150     /** Verbose equivalent to <tt>attach (NULL)</tt>. */
    151     void detach() { attach(NULL); }
     97    bool autoInitSpanConstructor(State aExpectedState);
     98    void autoInitSpanDestructor(State aNewState);
     99    State autoUninitSpanConstructor();
     100    void autoUninitSpanDestructor();
    152101
    153102private:
     103    ObjectState();
    154104
    155     DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoCallerBase)
    156     DECLARE_CLS_NEW_DELETE_NOOP(AutoCallerBase)
     105    void setState(State aState);
    157106
     107    /** Pointer to the managed object, mostly for error signalling or debugging
     108     * purposes, not used much. Guaranteed to be valid during the lifetime of
     109     * this object, no need to mess with refcount. */
    158110    VirtualBoxBase *mObj;
    159     HRESULT mRC;
    160     VirtualBoxBase::State mState;
     111    /** Primary state of this object */
     112    State mState;
     113    /** Thread that caused the last state change */
     114    RTTHREAD mStateChangeThread;
     115    /** Total number of active calls to this object */
     116    unsigned mCallers;
     117    /** Posted when the number of callers drops to zero */
     118    RTSEMEVENT mZeroCallersSem;
     119    /** Posted when the object goes from InInit/InUninit to some other state */
     120    RTSEMEVENTMULTI mInitUninitSem;
     121    /** Number of threads waiting for mInitUninitDoneSem */
     122    unsigned mInitUninitWaiters;
     123
     124    /** Protects access to state related data members */
     125    util::RWLockHandle mStateLock;
    161126};
    162127
    163 /**
    164  * Smart class that automatically increases the number of normal
    165  * (non-limited) callers of the given VirtualBoxBase object when an instance
    166  * is constructed and decreases it back when the created instance goes out
    167  * of scope (i.e. gets destroyed).
    168  *
    169  * A typical usage pattern to declare a normal method of some object (i.e. a
    170  * method that is valid only when the object provides its full
    171  * functionality) is:
    172  * <code>
    173  * STDMETHODIMP Component::Foo()
    174  * {
    175  *     AutoCaller autoCaller(this);
    176  *     HRESULT hrc = autoCaller.rc();
    177  *     if (SUCCEEDED(hrc))
    178  *     {
    179  *         ...
    180  *     }
    181  *     return hrc;
    182  * }
    183  * </code>
    184  *
    185  * Using this class is equivalent to using the AutoCallerBase template with
    186  * the @a aLimited argument set to |false|, but this class is preferred
    187  * because provides better self-descriptiveness.
    188  *
    189  * See AutoCallerBase for more information about auto caller functionality.
    190  */
    191 typedef AutoCallerBase<false> AutoCaller;
    192 
    193 /**
    194  * Smart class that automatically increases the number of limited callers of
    195  * the given VirtualBoxBase object when an instance is constructed and
    196  * decreases it back when the created instance goes out of scope (i.e. gets
    197  * destroyed).
    198  *
    199  * A typical usage pattern to declare a limited method of some object (i.e.
    200  * a method that is valid even if the object doesn't provide its full
    201  * functionality) is:
    202  * <code>
    203  * STDMETHODIMP Component::Bar()
    204  * {
    205  *     AutoLimitedCaller autoCaller(this);
    206  *     HRESULT hrc = autoCaller.rc();
    207  *     if (SUCCEEDED(hrc))
    208  *     {
    209  *         ...
    210  *     }
    211  *     return hrc;
    212  * </code>
    213  *
    214  * Using this class is equivalent to using the AutoCallerBase template with
    215  * the @a aLimited argument set to |true|, but this class is preferred
    216  * because provides better self-descriptiveness.
    217  *
    218  * See AutoCallerBase for more information about auto caller functionality.
    219  */
    220 typedef AutoCallerBase<true> AutoLimitedCaller;
    221 
    222 /**
    223  * Smart class to enclose the state transition NotReady->InInit->Ready.
    224  *
    225  * The purpose of this span is to protect object initialization.
    226  *
    227  * Instances must be created as a stack-based variable taking |this| pointer
    228  * as the argument at the beginning of init() methods of VirtualBoxBase
    229  * subclasses. When this variable is created it automatically places the
    230  * object to the InInit state.
    231  *
    232  * When the created variable goes out of scope (i.e. gets destroyed) then,
    233  * depending on the result status of this initialization span, it either
    234  * places the object to Ready or Limited state or calls the object's
    235  * VirtualBoxBase::uninit() method which is supposed to place the object
    236  * back to the NotReady state using the AutoUninitSpan class.
    237  *
    238  * The initial result status of the initialization span is determined by the
    239  * @a aResult argument of the AutoInitSpan constructor (Result::Failed by
    240  * default). Inside the initialization span, the success status can be set
    241  * to Result::Succeeded using #setSucceeded(), to to Result::Limited using
    242  * #setLimited() or to Result::Failed using #setFailed(). Please don't
    243  * forget to set the correct success status before getting the AutoInitSpan
    244  * variable destroyed (for example, by performing an early return from
    245  * the init() method)!
    246  *
    247  * Note that if an instance of this class gets constructed when the object
    248  * is in the state other than NotReady, #isOk() returns |false| and methods
    249  * of this class do nothing: the state transition is not performed.
    250  *
    251  * A typical usage pattern is:
    252  * <code>
    253  * HRESULT Component::init()
    254  * {
    255  *     AutoInitSpan autoInitSpan(this);
    256  *     AssertReturn(autoInitSpan.isOk(), E_FAIL);
    257  *     ...
    258  *     if (FAILED(rc))
    259  *         return rc;
    260  *     ...
    261  *     if (SUCCEEDED(rc))
    262  *         autoInitSpan.setSucceeded();
    263  *     return rc;
    264  * }
    265  * </code>
    266  *
    267  * @note Never create instances of this class outside init() methods of
    268  *       VirtualBoxBase subclasses and never pass anything other than |this|
    269  *       as the argument to the constructor!
    270  */
    271 class AutoInitSpan
    272 {
    273 public:
    274 
    275     enum Result { Failed = 0x0, Succeeded = 0x1, Limited = 0x2 };
    276 
    277     AutoInitSpan(VirtualBoxBase *aObj, Result aResult = Failed);
    278     ~AutoInitSpan();
    279 
    280     /**
    281      * Returns |true| if this instance has been created at the right moment
    282      * (when the object was in the NotReady state) and |false| otherwise.
    283      */
    284     bool isOk() const { return mOk; }
    285 
    286     /**
    287      * Sets the initialization status to Succeeded to indicates successful
    288      * initialization. The AutoInitSpan destructor will place the managed
    289      * VirtualBoxBase object to the Ready state.
    290      */
    291     void setSucceeded() { mResult = Succeeded; }
    292 
    293     /**
    294      * Sets the initialization status to Succeeded to indicate limited
    295      * (partly successful) initialization. The AutoInitSpan destructor will
    296      * place the managed VirtualBoxBase object to the Limited state.
    297      */
    298     void setLimited() { mResult = Limited; }
    299 
    300     /**
    301      * Sets the initialization status to Failure to indicates failed
    302      * initialization. The AutoInitSpan destructor will place the managed
    303      * VirtualBoxBase object to the InitFailed state and will automatically
    304      * call its uninit() method which is supposed to place the object back
    305      * to the NotReady state using AutoUninitSpan.
    306      */
    307     void setFailed() { mResult = Failed; }
    308 
    309     /** Returns the current initialization result. */
    310     Result result() { return mResult; }
    311 
    312 private:
    313 
    314     DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoInitSpan)
    315     DECLARE_CLS_NEW_DELETE_NOOP(AutoInitSpan)
    316 
    317     VirtualBoxBase *mObj;
    318     Result mResult : 3; // must be at least total number of bits + 1 (sign)
    319     bool mOk : 1;
    320 };
    321 
    322 /**
    323  * Smart class to enclose the state transition Limited->InInit->Ready.
    324  *
    325  * The purpose of this span is to protect object re-initialization.
    326  *
    327  * Instances must be created as a stack-based variable taking |this| pointer
    328  * as the argument at the beginning of methods of VirtualBoxBase
    329  * subclasses that try to re-initialize the object to bring it to the Ready
    330  * state (full functionality) after partial initialization (limited
    331  * functionality). When this variable is created, it automatically places
    332  * the object to the InInit state.
    333  *
    334  * When the created variable goes out of scope (i.e. gets destroyed),
    335  * depending on the success status of this initialization span, it either
    336  * places the object to the Ready state or brings it back to the Limited
    337  * state.
    338  *
    339  * The initial success status of the re-initialization span is |false|. In
    340  * order to make it successful, #setSucceeded() must be called before the
    341  * instance is destroyed.
    342  *
    343  * Note that if an instance of this class gets constructed when the object
    344  * is in the state other than Limited, #isOk() returns |false| and methods
    345  * of this class do nothing: the state transition is not performed.
    346  *
    347  * A typical usage pattern is:
    348  * <code>
    349  * HRESULT Component::reinit()
    350  * {
    351  *     AutoReinitSpan autoReinitSpan (this);
    352  *     AssertReturn (autoReinitSpan.isOk(), E_FAIL);
    353  *     ...
    354  *     if (FAILED(rc))
    355  *         return rc;
    356  *     ...
    357  *     if (SUCCEEDED(rc))
    358  *         autoReinitSpan.setSucceeded();
    359  *     return rc;
    360  * }
    361  * </code>
    362  *
    363  * @note Never create instances of this class outside re-initialization
    364  * methods of VirtualBoxBase subclasses and never pass anything other than
    365  * |this| as the argument to the constructor!
    366  */
    367 class AutoReinitSpan
    368 {
    369 public:
    370 
    371     AutoReinitSpan(VirtualBoxBase *aObj);
    372     ~AutoReinitSpan();
    373 
    374     /**
    375      * Returns |true| if this instance has been created at the right moment
    376      * (when the object was in the Limited state) and |false| otherwise.
    377      */
    378     bool isOk() const { return mOk; }
    379 
    380     /**
    381      * Sets the re-initialization status to Succeeded to indicates
    382      * successful re-initialization. The AutoReinitSpan destructor will place
    383      * the managed VirtualBoxBase object to the Ready state.
    384      */
    385     void setSucceeded() { mSucceeded = true; }
    386 
    387 private:
    388 
    389     DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoReinitSpan)
    390     DECLARE_CLS_NEW_DELETE_NOOP(AutoReinitSpan)
    391 
    392     VirtualBoxBase *mObj;
    393     bool mSucceeded : 1;
    394     bool mOk : 1;
    395 };
    396 
    397 /**
    398  * Smart class to enclose the state transition Ready->InUninit->NotReady,
    399  * InitFailed->InUninit->NotReady.
    400  *
    401  * The purpose of this span is to protect object uninitialization.
    402  *
    403  * Instances must be created as a stack-based variable taking |this| pointer
    404  * as the argument at the beginning of uninit() methods of VirtualBoxBase
    405  * subclasses. When this variable is created it automatically places the
    406  * object to the InUninit state, unless it is already in the NotReady state
    407  * as indicated by #uninitDone() returning |true|. In the latter case, the
    408  * uninit() method must immediately return because there should be nothing
    409  * to uninitialize.
    410  *
    411  * When this variable goes out of scope (i.e. gets destroyed), it places the
    412  * object to NotReady state.
    413  *
    414  * A typical usage pattern is:
    415  * <code>
    416  * void Component::uninit()
    417  * {
    418  *     AutoUninitSpan autoUninitSpan (this);
    419  *     if (autoUninitSpan.uninitDone())
    420  *         return;
    421  *     ...
    422  * }
    423  * </code>
    424  *
    425  * @note The constructor of this class blocks the current thread execution
    426  *       until the number of callers added to the object using #addCaller()
    427  *       or AutoCaller drops to zero. For this reason, it is forbidden to
    428  *       create instances of this class (or call uninit()) within the
    429  *       AutoCaller or #addCaller() scope because it is a guaranteed
    430  *       deadlock.
    431  *
    432  * @note Never create instances of this class outside uninit() methods and
    433  *       never pass anything other than |this| as the argument to the
    434  *       constructor!
    435  */
    436 class AutoUninitSpan
    437 {
    438 public:
    439 
    440     AutoUninitSpan(VirtualBoxBase *aObj);
    441     ~AutoUninitSpan();
    442 
    443     /** |true| when uninit() is called as a result of init() failure */
    444     bool initFailed() { return mInitFailed; }
    445 
    446     /** |true| when uninit() has already been called (so the object is NotReady) */
    447     bool uninitDone() { return mUninitDone; }
    448 
    449 private:
    450 
    451     DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoUninitSpan)
    452     DECLARE_CLS_NEW_DELETE_NOOP(AutoUninitSpan)
    453 
    454     VirtualBoxBase *mObj;
    455     bool mInitFailed : 1;
    456     bool mUninitDone : 1;
    457 };
    458 
    459 #endif // !____H_AUTOCALLER
     128#endif // !____H_OBJECTSTATE
  • trunk/src/VBox/Main/include/VirtualBoxBase.h

    r51337 r51903  
    44
    55/*
    6  * Copyright (C) 2006-2013 Oracle Corporation
     6 * Copyright (C) 2006-2014 Oracle Corporation
    77 *
    88 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    2424#include <map>
    2525
     26#include "ObjectState.h"
     27
    2628#include "VBox/com/AutoLock.h"
    2729#include "VBox/com/string.h"
     
    3032#include "VBox/com/VirtualBox.h"
    3133
    32 // avoid including VBox/settings.h and VBox/xml.h;
    33 // only declare the classes
     34// avoid including VBox/settings.h and VBox/xml.h; only declare the classes
    3435namespace xml
    3536{
     
    4445using namespace com;
    4546using namespace util;
    46 
    47 class AutoInitSpan;
    48 class AutoUninitSpan;
    4947
    5048class VirtualBox;
     
    665663 * Declares functionality that should be available in all components.
    666664 *
    667  * Among the basic functionality implemented by this class is the primary object
    668  * state that indicates if the object is ready to serve the calls, and if not,
    669  * what stage it is currently at. Here is the primary state diagram:
    670  *
    671  *              +-------------------------------------------------------+
    672  *              |                                                       |
    673  *              |         (InitFailed) -----------------------+         |
    674  *              |              ^                              |         |
    675  *              v              |                              v         |
    676  *  [*] ---> NotReady ----> (InInit) -----> Ready -----> (InUninit) ----+
    677  *                     ^       |
    678  *                     |       v
    679  *                     |    Limited
    680  *                     |       |
    681  *                     +-------+
    682  *
    683  * The object is fully operational only when its state is Ready. The Limited
    684  * state means that only some vital part of the object is operational, and it
    685  * requires some sort of reinitialization to become fully operational. The
    686  * NotReady state means the object is basically dead: it either was not yet
    687  * initialized after creation at all, or was uninitialized and is waiting to be
    688  * destroyed when the last reference to it is released. All other states are
    689  * transitional.
    690  *
    691  * The NotReady->InInit->Ready, NotReady->InInit->Limited and
    692  * NotReady->InInit->InitFailed transition is done by the AutoInitSpan smart
    693  * class.
    694  *
    695  * The Limited->InInit->Ready, Limited->InInit->Limited and
    696  * Limited->InInit->InitFailed transition is done by the AutoReinitSpan smart
    697  * class.
    698  *
    699  * The Ready->InUninit->NotReady and InitFailed->InUninit->NotReady
    700  * transitions are done by the AutoUninitSpan smart class.
    701  *
    702  * In order to maintain the primary state integrity and declared functionality
    703  * all subclasses must:
    704  *
    705  * 1) Use the above Auto*Span classes to perform state transitions. See the
    706  *    individual class descriptions for details.
    707  *
    708  * 2) All public methods of subclasses (i.e. all methods that can be called
    709  *    directly, not only from within other methods of the subclass) must have a
    710  *    standard prolog as described in the AutoCaller and AutoLimitedCaller
    711  *    documentation. Alternatively, they must use addCaller()/releaseCaller()
    712  *    directly (and therefore have both the prolog and the epilog), but this is
    713  *    not recommended.
     665 * The object state logic is documented in ObjectState.h.
    714666 */
    715667class ATL_NO_VTABLE VirtualBoxBase
     
    744696
    745697public:
    746     enum State { NotReady, Ready, InInit, InUninit, InitFailed, Limited };
    747 
    748698    VirtualBoxBase();
    749699    virtual ~VirtualBoxBase();
     
    756706     *
    757707     * @note Never call this method the AutoCaller scope or after the
    758      *       #addCaller() call not paired by #releaseCaller() because it is a
    759      *       guaranteed deadlock. See AutoUninitSpan for details.
     708     *       ObjectState::addCaller() call not paired by
     709     *       ObjectState::releaseCaller() because it is a guaranteed deadlock.
     710     *       See AutoUninitSpan and AutoCaller.h/ObjectState.h for details.
    760711     */
    761712    virtual void uninit()
    762713    { }
    763714
    764     virtual HRESULT addCaller(State *aState = NULL,
    765                               bool aLimited = false);
    766     virtual void releaseCaller();
    767 
    768     /**
    769      * Adds a limited caller. This method is equivalent to doing
    770      * <tt>addCaller(aState, true)</tt>, but it is preferred because provides
    771      * better self-descriptiveness. See #addCaller() for more info.
    772      */
    773     HRESULT addLimitedCaller(State *aState = NULL)
    774     {
    775         return addCaller(aState, true /* aLimited */);
     715    /**
     716     */
     717    ObjectState &getObjectState()
     718    {
     719        return mState;
    776720    }
    777721
     
    805749
    806750    virtual RWLockHandle *lockHandle() const;
    807 
    808     /**
    809      * Returns a lock handle used to protect the primary state fields (used by
    810      * #addCaller(), AutoInitSpan, AutoUninitSpan, etc.). Only intended to be
    811      * used for similar purposes in subclasses. WARNING: NO any other locks may
    812      * be requested while holding this lock!
    813      */
    814     WriteLockHandle *stateLockHandle() { return &mStateLock; }
    815751
    816752    static HRESULT handleUnexpectedExceptions(VirtualBoxBase *const aThis, RT_SRC_POS_DECL);
     
    851787
    852788private:
    853 
    854     void setState(State aState)
    855     {
    856         Assert(mState != aState);
    857         mState = aState;
    858         mStateChangeThread = RTThreadSelf();
    859     }
    860 
    861     /** Primary state of this object */
    862     State mState;
    863     /** Thread that caused the last state change */
    864     RTTHREAD mStateChangeThread;
    865     /** Total number of active calls to this object */
    866     unsigned mCallers;
    867     /** Posted when the number of callers drops to zero */
    868     RTSEMEVENT mZeroCallersSem;
    869     /** Posted when the object goes from InInit/InUninit to some other state */
    870     RTSEMEVENTMULTI mInitUninitSem;
    871     /** Number of threads waiting for mInitUninitDoneSem */
    872     unsigned mInitUninitWaiters;
    873 
    874     /** Protects access to state related data members */
    875     WriteLockHandle mStateLock;
     789    /** Object for representing object state */
     790    ObjectState mState;
    876791
    877792    /** User-level object lock for subclasses */
    878793    mutable RWLockHandle *mObjectLock;
    879 
    880     friend class AutoInitSpan;
    881     friend class AutoReinitSpan;
    882     friend class AutoUninitSpan;
    883794};
    884795
  • trunk/src/VBox/Main/src-all/AutoCaller.cpp

    r51732 r51903  
    33/** @file
    44 *
    5  * VirtualBox COM base classes implementation
     5 * VirtualBox object state implementation
    66 */
    77
    88/*
    9  * Copyright (C) 2006-2012 Oracle Corporation
     9 * Copyright (C) 2006-2014 Oracle Corporation
    1010 *
    1111 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    1919
    2020#include <iprt/semaphore.h>
    21 #include <iprt/asm.h>
    22 #include <iprt/cpp/exception.h>
    23 
    24 #include <typeinfo>
    25 
    26 #if !defined (VBOX_WITH_XPCOM)
    27 #include <windows.h>
    28 #include <dbghelp.h>
    29 #else /* !defined (VBOX_WITH_XPCOM) */
    30 /// @todo remove when VirtualBoxErrorInfo goes away from here
    31 #include <nsIServiceManager.h>
    32 #include <nsIExceptionService.h>
    33 #endif /* !defined (VBOX_WITH_XPCOM) */
    3421
    3522#include "VirtualBoxBase.h"
    3623#include "AutoCaller.h"
    37 #include "VirtualBoxErrorInfoImpl.h"
    3824#include "Logging.h"
    3925
    40 #include "VBox/com/ErrorInfo.h"
    41 #include "VBox/com/MultiResult.h"
    4226
    4327////////////////////////////////////////////////////////////////////////////////
    4428//
    45 // VirtualBoxBase
     29// ObjectState methods
    4630//
    4731////////////////////////////////////////////////////////////////////////////////
    4832
    49 VirtualBoxBase::VirtualBoxBase()
    50     : mStateLock(LOCKCLASS_OBJECTSTATE)
    51 {
     33
     34ObjectState::ObjectState() : mStateLock(LOCKCLASS_OBJECTSTATE)
     35{
     36    AssertFailed();
     37}
     38
     39ObjectState::ObjectState(VirtualBoxBase *aObj) :
     40    mStateLock(LOCKCLASS_OBJECTSTATE), mObj(aObj)
     41{
     42    Assert(mObj);
    5243    mState = NotReady;
    5344    mStateChangeThread = NIL_RTTHREAD;
     
    5647    mInitUninitSem = NIL_RTSEMEVENTMULTI;
    5748    mInitUninitWaiters = 0;
    58     mObjectLock = NULL;
    59 }
    60 
    61 VirtualBoxBase::~VirtualBoxBase()
    62 {
    63     if (mObjectLock)
    64         delete mObjectLock;
     49}
     50
     51ObjectState::~ObjectState()
     52{
    6553    Assert(mInitUninitWaiters == 0);
    6654    Assert(mInitUninitSem == NIL_RTSEMEVENTMULTI);
    6755    if (mZeroCallersSem != NIL_RTSEMEVENT)
    68         RTSemEventDestroy (mZeroCallersSem);
     56        RTSemEventDestroy(mZeroCallersSem);
    6957    mCallers = 0;
    7058    mStateChangeThread = NIL_RTTHREAD;
    7159    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 */
    87 RWLockHandle *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(&mObjectLock, objLock, NULL))
    98         {
    99             delete objLock;
    100             objLock = ASMAtomicReadPtrT(&mObjectLock, RWLockHandle *);
    101         }
    102         return objLock;
    103     }
    104     return mObjectLock;
     60    mObj = NULL;
     61}
     62
     63ObjectState::State ObjectState::getState()
     64{
     65    AutoReadLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
     66    return mState;
    10567}
    10668
     
    151113 * and should return the failed result code to its own caller.
    152114 *
    153  * @param aState        Where to store the current object's state (can be
    154  *                      used in overridden methods to determine the cause of
    155  *                      the failure).
    156115 * @param aLimited      |true| to add a limited caller.
    157116 *
    158117 * @return              S_OK on success or E_ACCESSDENIED on failure.
    159118 *
    160  * @note It is preferable to use the #addLimitedCaller() rather than
    161  *       calling this method with @a aLimited = |true|, for better
    162  *       self-descriptiveness.
    163  *
    164  * @sa #addLimitedCaller()
    165119 * @sa #releaseCaller()
    166120 */
    167 HRESULT VirtualBoxBase::addCaller(State *aState /* = NULL */,
    168                                   bool aLimited /* = false */)
     121HRESULT ObjectState::addCaller(bool aLimited /* = false */)
    169122{
    170123    AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
     
    175128    {
    176129        /* if Ready or allows Limited, increase the number of callers */
    177         ++ mCallers;
     130        ++mCallers;
    178131        rc = S_OK;
    179132    }
     
    199152             * uninit() is called.
    200153             */
    201             ++ mCallers;
     154            ++mCallers;
    202155
    203156            /* lazy semaphore creation */
    204157            if (mInitUninitSem == NIL_RTSEMEVENTMULTI)
    205158            {
    206                 RTSemEventMultiCreate (&mInitUninitSem);
     159                RTSemEventMultiCreate(&mInitUninitSem);
    207160                Assert(mInitUninitWaiters == 0);
    208161            }
    209162
    210             ++ mInitUninitWaiters;
     163            ++mInitUninitWaiters;
    211164
    212165            LogFlowThisFunc(("Waiting for AutoInitSpan/AutoReinitSpan to finish...\n"));
    213166
    214167            stateLock.release();
    215             RTSemEventMultiWait (mInitUninitSem, RT_INDEFINITE_WAIT);
     168            RTSemEventMultiWait(mInitUninitSem, RT_INDEFINITE_WAIT);
    216169            stateLock.acquire();
    217170
    218             if (-- mInitUninitWaiters == 0)
     171            if (--mInitUninitWaiters == 0)
    219172            {
    220173                /* destroy the semaphore since no more necessary */
    221                 RTSemEventMultiDestroy (mInitUninitSem);
     174                RTSemEventMultiDestroy(mInitUninitSem);
    222175                mInitUninitSem = NIL_RTSEMEVENTMULTI;
    223176            }
     
    228181            {
    229182                Assert(mCallers != 0);
    230                 -- mCallers;
     183                --mCallers;
    231184                if (mCallers == 0 && mState == InUninit)
    232185                {
     
    238191    }
    239192
    240     if (aState)
    241         *aState = mState;
    242 
    243193    if (FAILED(rc))
    244194    {
    245         if (mState == VirtualBoxBase::Limited)
    246             rc = setError(rc, "The object functionality is limited");
     195        if (mState == Limited)
     196            rc = mObj->setError(rc, "The object functionality is limited");
    247197        else
    248             rc = setError(rc, "The object is not ready");
     198            rc = mObj->setError(rc, "The object is not ready");
    249199    }
    250200
     
    255205 * Decreases the number of calls to this object by one.
    256206 *
    257  * Must be called after every #addCaller() or #addLimitedCaller() when
    258  * protecting the object from uninitialization is no more necessary.
    259  */
    260 void VirtualBoxBase::releaseCaller()
     207 * Must be called after every #addCaller() when protecting the object
     208 * from uninitialization is no more necessary.
     209 */
     210void ObjectState::releaseCaller()
    261211{
    262212    AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
     
    294244    }
    295245
    296     AssertMsgFailed (("mState = %d!", mState));
    297 }
    298 
    299 /**
    300  * Handles unexpected exceptions by turning them into COM errors in release
    301  * builds or by hitting a breakpoint in the release builds.
    302  *
    303  * Usage pattern:
    304  * @code
    305         try
    306         {
    307             // ...
    308         }
    309         catch (LaLalA)
    310         {
    311             // ...
    312         }
    313         catch (...)
    314         {
    315             rc = VirtualBox::handleUnexpectedExceptions(this, RT_SRC_POS);
    316         }
    317  * @endcode
    318  *
    319  * @param aThis             object where the exception happened
    320  * @param RT_SRC_POS_DECL   "RT_SRC_POS" macro instantiation.
    321  *  */
    322 /* static */
    323 HRESULT VirtualBoxBase::handleUnexpectedExceptions(VirtualBoxBase *const aThis, RT_SRC_POS_DECL)
    324 {
    325     try
    326     {
    327         /* re-throw the current exception */
    328         throw;
    329     }
    330     catch (const RTCError &err)      // includes all XML exceptions
    331     {
    332         return setErrorInternal(E_FAIL, aThis->getClassIID(), aThis->getComponentName(),
    333                                 Utf8StrFmt(tr("%s.\n%s[%d] (%s)"),
    334                                            err.what(),
    335                                            pszFile, iLine, pszFunction).c_str(),
    336                                 false /* aWarning */,
    337                                 true /* aLogIt */);
    338     }
    339     catch (const std::exception &err)
    340     {
    341         return setErrorInternal(E_FAIL, aThis->getClassIID(), aThis->getComponentName(),
    342                                 Utf8StrFmt(tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
    343                                            err.what(), typeid(err).name(),
    344                                            pszFile, iLine, pszFunction).c_str(),
    345                                 false /* aWarning */,
    346                                 true /* aLogIt */);
    347     }
    348     catch (...)
    349     {
    350         return setErrorInternal(E_FAIL, aThis->getClassIID(), aThis->getComponentName(),
    351                                 Utf8StrFmt(tr("Unknown exception\n%s[%d] (%s)"),
    352                                            pszFile, iLine, pszFunction).c_str(),
    353                                 false /* aWarning */,
    354                                 true /* aLogIt */);
    355     }
    356 
    357     /* should not get here */
    358     AssertFailed();
    359     return E_FAIL;
    360 }
    361 
    362 /**
    363  *  Sets error info for the current thread. This is an internal function that
    364  *  gets eventually called by all public variants.  If @a aWarning is
    365  *  @c true, then the highest (31) bit in the @a aResultCode value which
    366  *  indicates the error severity is reset to zero to make sure the receiver will
    367  *  recognize that the created error info object represents a warning rather
    368  *  than an error.
    369  */
    370 /* static */
    371 HRESULT VirtualBoxBase::setErrorInternal(HRESULT aResultCode,
    372                                          const GUID &aIID,
    373                                          const char *pcszComponent,
    374                                          Utf8Str aText,
    375                                          bool aWarning,
    376                                          bool aLogIt)
    377 {
    378     /* whether multi-error mode is turned on */
    379     bool preserve = MultiResult::isMultiEnabled();
    380 
    381     if (aLogIt)
    382         LogRel(("%s [COM]: aRC=%Rhrc (%#08x) aIID={%RTuuid} aComponent={%s} aText={%s}, preserve=%RTbool\n",
    383                 aWarning ? "WARNING" : "ERROR",
    384                 aResultCode,
    385                 aResultCode,
    386                 &aIID,
    387                 pcszComponent,
    388                 aText.c_str(),
    389                 aWarning,
    390                 preserve));
    391 
    392     /* these are mandatory, others -- not */
    393     AssertReturn((!aWarning && FAILED(aResultCode)) ||
    394                   (aWarning && aResultCode != S_OK),
    395                   E_FAIL);
    396 
    397     /* reset the error severity bit if it's a warning */
    398     if (aWarning)
    399         aResultCode &= ~0x80000000;
    400 
    401     HRESULT rc = S_OK;
    402 
    403     if (aText.isEmpty())
    404     {
    405         /* Some default info */
    406         switch (aResultCode)
    407         {
    408             case E_INVALIDARG:                 aText = "A parameter has an invalid value"; break;
    409             case E_POINTER:                    aText = "A parameter is an invalid pointer"; break;
    410             case E_UNEXPECTED:                 aText = "The result of the operation is unexpected"; break;
    411             case E_ACCESSDENIED:               aText = "The access to an object is not allowed"; break;
    412             case E_OUTOFMEMORY:                aText = "The allocation of new memory failed"; break;
    413             case E_NOTIMPL:                    aText = "The requested operation is not implemented"; break;
    414             case E_NOINTERFACE:                aText = "The requested interface is not implemented"; break;
    415             case E_FAIL:                       aText = "A general error occurred"; break;
    416             case E_ABORT:                      aText = "The operation was canceled"; break;
    417             case VBOX_E_OBJECT_NOT_FOUND:      aText = "Object corresponding to the supplied arguments does not exist"; break;
    418             case VBOX_E_INVALID_VM_STATE:      aText = "Current virtual machine state prevents the operation"; break;
    419             case VBOX_E_VM_ERROR:              aText = "Virtual machine error occurred attempting the operation"; break;
    420             case VBOX_E_FILE_ERROR:            aText = "File not accessible or erroneous file contents"; break;
    421             case VBOX_E_IPRT_ERROR:            aText = "Runtime subsystem error"; break;
    422             case VBOX_E_PDM_ERROR:             aText = "Pluggable Device Manager error"; break;
    423             case VBOX_E_INVALID_OBJECT_STATE:  aText = "Current object state prohibits operation"; break;
    424             case VBOX_E_HOST_ERROR:            aText = "Host operating system related error"; break;
    425             case VBOX_E_NOT_SUPPORTED:         aText = "Requested operation is not supported"; break;
    426             case VBOX_E_XML_ERROR:             aText = "Invalid XML found"; break;
    427             case VBOX_E_INVALID_SESSION_STATE: aText = "Current session state prohibits operation"; break;
    428             case VBOX_E_OBJECT_IN_USE:         aText = "Object being in use prohibits operation"; break;
    429             default:                           aText = "Unknown error"; break;
    430         }
    431     }
    432 
    433     do
    434     {
    435         ComObjPtr<VirtualBoxErrorInfo> info;
    436         rc = info.createObject();
    437         if (FAILED(rc)) break;
    438 
    439 #if !defined (VBOX_WITH_XPCOM)
    440 
    441         ComPtr<IVirtualBoxErrorInfo> curInfo;
    442         if (preserve)
    443         {
    444             /* get the current error info if any */
    445             ComPtr<IErrorInfo> err;
    446             rc = ::GetErrorInfo (0, err.asOutParam());
    447             if (FAILED(rc)) break;
    448             rc = err.queryInterfaceTo(curInfo.asOutParam());
    449             if (FAILED(rc))
    450             {
    451                 /* create a IVirtualBoxErrorInfo wrapper for the native
    452                  * IErrorInfo object */
    453                 ComObjPtr<VirtualBoxErrorInfo> wrapper;
    454                 rc = wrapper.createObject();
    455                 if (SUCCEEDED(rc))
    456                 {
    457                     rc = wrapper->init (err);
    458                     if (SUCCEEDED(rc))
    459                         curInfo = wrapper;
    460                 }
    461             }
    462         }
    463         /* On failure, curInfo will stay null */
    464         Assert(SUCCEEDED(rc) || curInfo.isNull());
    465 
    466         /* set the current error info and preserve the previous one if any */
    467         rc = info->init(aResultCode, aIID, pcszComponent, aText, curInfo);
    468         if (FAILED(rc)) break;
    469 
    470         ComPtr<IErrorInfo> err;
    471         rc = info.queryInterfaceTo(err.asOutParam());
    472         if (SUCCEEDED(rc))
    473             rc = ::SetErrorInfo (0, err);
    474 
    475 #else // !defined (VBOX_WITH_XPCOM)
    476 
    477         nsCOMPtr <nsIExceptionService> es;
    478         es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
    479         if (NS_SUCCEEDED(rc))
    480         {
    481             nsCOMPtr <nsIExceptionManager> em;
    482             rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
    483             if (FAILED(rc)) break;
    484 
    485             ComPtr<IVirtualBoxErrorInfo> curInfo;
    486             if (preserve)
    487             {
    488                 /* get the current error info if any */
    489                 ComPtr<nsIException> ex;
    490                 rc = em->GetCurrentException (ex.asOutParam());
    491                 if (FAILED(rc)) break;
    492                 rc = ex.queryInterfaceTo(curInfo.asOutParam());
    493                 if (FAILED(rc))
    494                 {
    495                     /* create a IVirtualBoxErrorInfo wrapper for the native
    496                      * nsIException object */
    497                     ComObjPtr<VirtualBoxErrorInfo> wrapper;
    498                     rc = wrapper.createObject();
    499                     if (SUCCEEDED(rc))
    500                     {
    501                         rc = wrapper->init (ex);
    502                         if (SUCCEEDED(rc))
    503                             curInfo = wrapper;
    504                     }
    505                 }
    506             }
    507             /* On failure, curInfo will stay null */
    508             Assert(SUCCEEDED(rc) || curInfo.isNull());
    509 
    510             /* set the current error info and preserve the previous one if any */
    511             rc = info->init(aResultCode, aIID, pcszComponent, Bstr(aText), curInfo);
    512             if (FAILED(rc)) break;
    513 
    514             ComPtr<nsIException> ex;
    515             rc = info.queryInterfaceTo(ex.asOutParam());
    516             if (SUCCEEDED(rc))
    517                 rc = em->SetCurrentException (ex);
    518         }
    519         else if (rc == NS_ERROR_UNEXPECTED)
    520         {
    521             /*
    522              *  It is possible that setError() is being called by the object
    523              *  after the XPCOM shutdown sequence has been initiated
    524              *  (for example, when XPCOM releases all instances it internally
    525              *  references, which can cause object's FinalConstruct() and then
    526              *  uninit()). In this case, do_GetService() above will return
    527              *  NS_ERROR_UNEXPECTED and it doesn't actually make sense to
    528              *  set the exception (nobody will be able to read it).
    529              */
    530             LogWarningFunc(("Will not set an exception because nsIExceptionService is not available "
    531                             "(NS_ERROR_UNEXPECTED). XPCOM is being shutdown?\n"));
    532             rc = NS_OK;
    533         }
    534 
    535 #endif // !defined (VBOX_WITH_XPCOM)
    536     }
    537     while (0);
    538 
    539     AssertComRC (rc);
    540 
    541     return SUCCEEDED(rc) ? aResultCode : rc;
    542 }
    543 
    544 /**
    545  * Shortcut instance method to calling the static setErrorInternal with the
    546  * class interface ID and component name inserted correctly. This uses the
    547  * virtual getClassIID() and getComponentName() methods which are automatically
    548  * defined by the VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT macro.
    549  * @param aResultCode
    550  * @param pcsz
    551  * @return
    552  */
    553 HRESULT VirtualBoxBase::setError(HRESULT aResultCode)
    554 {
    555     return setErrorInternal(aResultCode,
    556                             this->getClassIID(),
    557                             this->getComponentName(),
    558                             "",
    559                             false /* aWarning */,
    560                             true /* aLogIt */);
    561 }
    562 
    563 /**
    564  * Shortcut instance method to calling the static setErrorInternal with the
    565  * class interface ID and component name inserted correctly. This uses the
    566  * virtual getClassIID() and getComponentName() methods which are automatically
    567  * defined by the VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT macro.
    568  * @param aResultCode
    569  * @return
    570  */
    571 HRESULT VirtualBoxBase::setError(HRESULT aResultCode, const char *pcsz, ...)
    572 {
    573     va_list args;
    574     va_start(args, pcsz);
    575     HRESULT rc = setErrorInternal(aResultCode,
    576                                   this->getClassIID(),
    577                                   this->getComponentName(),
    578                                   Utf8Str(pcsz, args),
    579                                   false /* aWarning */,
    580                                   true /* aLogIt */);
    581     va_end(args);
    582     return rc;
    583 }
    584 
    585 /**
    586  * Shortcut instance method to calling the static setErrorInternal with the
    587  * class interface ID and component name inserted correctly. This uses the
    588  * virtual getClassIID() and getComponentName() methods which are automatically
    589  * defined by the VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT macro.
    590  * @param ei
    591  * @return
    592  */
    593 HRESULT VirtualBoxBase::setError(const com::ErrorInfo &ei)
    594 {
    595     /* whether multi-error mode is turned on */
    596     bool preserve = MultiResult::isMultiEnabled();
    597 
    598     HRESULT rc = S_OK;
    599 
    600     do
    601     {
    602         ComObjPtr<VirtualBoxErrorInfo> info;
    603         rc = info.createObject();
    604         if (FAILED(rc)) break;
    605 
    606 #if !defined (VBOX_WITH_XPCOM)
    607 
    608         ComPtr<IVirtualBoxErrorInfo> curInfo;
    609         if (preserve)
    610         {
    611             /* get the current error info if any */
    612             ComPtr<IErrorInfo> err;
    613             rc = ::GetErrorInfo (0, err.asOutParam());
    614             if (FAILED(rc)) break;
    615             rc = err.queryInterfaceTo(curInfo.asOutParam());
    616             if (FAILED(rc))
    617             {
    618                 /* create a IVirtualBoxErrorInfo wrapper for the native
    619                  * IErrorInfo object */
    620                 ComObjPtr<VirtualBoxErrorInfo> wrapper;
    621                 rc = wrapper.createObject();
    622                 if (SUCCEEDED(rc))
    623                 {
    624                     rc = wrapper->init (err);
    625                     if (SUCCEEDED(rc))
    626                         curInfo = wrapper;
    627                 }
    628             }
    629         }
    630         /* On failure, curInfo will stay null */
    631         Assert(SUCCEEDED(rc) || curInfo.isNull());
    632 
    633         /* set the current error info and preserve the previous one if any */
    634         rc = info->init(ei, curInfo);
    635         if (FAILED(rc)) break;
    636 
    637         ComPtr<IErrorInfo> err;
    638         rc = info.queryInterfaceTo(err.asOutParam());
    639         if (SUCCEEDED(rc))
    640             rc = ::SetErrorInfo (0, err);
    641 
    642 #else // !defined (VBOX_WITH_XPCOM)
    643 
    644         nsCOMPtr <nsIExceptionService> es;
    645         es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
    646         if (NS_SUCCEEDED(rc))
    647         {
    648             nsCOMPtr <nsIExceptionManager> em;
    649             rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
    650             if (FAILED(rc)) break;
    651 
    652             ComPtr<IVirtualBoxErrorInfo> curInfo;
    653             if (preserve)
    654             {
    655                 /* get the current error info if any */
    656                 ComPtr<nsIException> ex;
    657                 rc = em->GetCurrentException (ex.asOutParam());
    658                 if (FAILED(rc)) break;
    659                 rc = ex.queryInterfaceTo(curInfo.asOutParam());
    660                 if (FAILED(rc))
    661                 {
    662                     /* create a IVirtualBoxErrorInfo wrapper for the native
    663                      * nsIException object */
    664                     ComObjPtr<VirtualBoxErrorInfo> wrapper;
    665                     rc = wrapper.createObject();
    666                     if (SUCCEEDED(rc))
    667                     {
    668                         rc = wrapper->init (ex);
    669                         if (SUCCEEDED(rc))
    670                             curInfo = wrapper;
    671                     }
    672                 }
    673             }
    674             /* On failure, curInfo will stay null */
    675             Assert(SUCCEEDED(rc) || curInfo.isNull());
    676 
    677             /* set the current error info and preserve the previous one if any */
    678             rc = info->init(ei, curInfo);
    679             if (FAILED(rc)) break;
    680 
    681             ComPtr<nsIException> ex;
    682             rc = info.queryInterfaceTo(ex.asOutParam());
    683             if (SUCCEEDED(rc))
    684                 rc = em->SetCurrentException (ex);
    685         }
    686         else if (rc == NS_ERROR_UNEXPECTED)
    687         {
    688             /*
    689              *  It is possible that setError() is being called by the object
    690              *  after the XPCOM shutdown sequence has been initiated
    691              *  (for example, when XPCOM releases all instances it internally
    692              *  references, which can cause object's FinalConstruct() and then
    693              *  uninit()). In this case, do_GetService() above will return
    694              *  NS_ERROR_UNEXPECTED and it doesn't actually make sense to
    695              *  set the exception (nobody will be able to read it).
    696              */
    697             LogWarningFunc(("Will not set an exception because nsIExceptionService is not available "
    698                             "(NS_ERROR_UNEXPECTED). XPCOM is being shutdown?\n"));
    699             rc = NS_OK;
    700         }
    701 
    702 #endif // !defined (VBOX_WITH_XPCOM)
    703     }
    704     while (0);
    705 
    706     AssertComRC (rc);
    707 
    708     return SUCCEEDED(rc) ? ei.getResultCode() : rc;
    709 }
    710 
    711 /**
    712  * Like setError(), but sets the "warning" bit in the call to setErrorInternal().
    713  * @param aResultCode
    714  * @param pcsz
    715  * @return
    716  */
    717 HRESULT VirtualBoxBase::setWarning(HRESULT aResultCode, const char *pcsz, ...)
    718 {
    719     va_list args;
    720     va_start(args, pcsz);
    721     HRESULT rc = setErrorInternal(aResultCode,
    722                                   this->getClassIID(),
    723                                   this->getComponentName(),
    724                                   Utf8Str(pcsz, args),
    725                                   true /* aWarning */,
    726                                   true /* aLogIt */);
    727     va_end(args);
    728     return rc;
    729 }
    730 
    731 /**
    732  * Like setError(), but disables the "log" flag in the call to setErrorInternal().
    733  * @param aResultCode
    734  * @param pcsz
    735  * @return
    736  */
    737 HRESULT VirtualBoxBase::setErrorNoLog(HRESULT aResultCode, const char *pcsz, ...)
    738 {
    739     va_list args;
    740     va_start(args, pcsz);
    741     HRESULT rc = setErrorInternal(aResultCode,
    742                                   this->getClassIID(),
    743                                   this->getComponentName(),
    744                                   Utf8Str(pcsz, args),
    745                                   false /* aWarning */,
    746                                   false /* aLogIt */);
    747     va_end(args);
    748     return rc;
    749 }
    750 
    751 /**
    752  * Clear the current error information.
    753  */
    754 /*static*/
    755 void VirtualBoxBase::clearError(void)
    756 {
    757 #if !defined(VBOX_WITH_XPCOM)
    758     ::SetErrorInfo (0, NULL);
    759 #else
    760     HRESULT rc = S_OK;
    761     nsCOMPtr <nsIExceptionService> es;
    762     es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
    763     if (NS_SUCCEEDED(rc))
    764     {
    765         nsCOMPtr <nsIExceptionManager> em;
    766         rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
    767         if (SUCCEEDED(rc))
    768             em->SetCurrentException(NULL);
    769     }
    770 #endif
     246    AssertMsgFailed(("mState = %d!", mState));
     247}
     248
     249bool ObjectState::autoInitSpanConstructor(ObjectState::State aExpectedState)
     250{
     251    AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
     252
     253    if (mState == aExpectedState)
     254    {
     255        setState(InInit);
     256        return true;
     257    }
     258    else
     259        return false;
     260}
     261
     262void ObjectState::autoInitSpanDestructor(State aNewState)
     263{
     264    AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
     265
     266    Assert(mState == InInit);
     267
     268    if (mCallers > 0 && mInitUninitWaiters > 0)
     269    {
     270        /* We have some pending addCaller() calls on other threads (created
     271         * during InInit), signal that InInit is finished and they may go on. */
     272        RTSemEventMultiSignal(mInitUninitSem);
     273    }
     274
     275    setState(aNewState);
     276}
     277
     278ObjectState::State ObjectState::autoUninitSpanConstructor()
     279{
     280    AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
     281
     282    Assert(mState != InInit);
     283
     284    if (mState == NotReady)
     285    {
     286        /* do nothing if already uninitialized */
     287        return mState;
     288    }
     289    else if (mState == InUninit)
     290    {
     291        /* Another thread has already started uninitialization, wait for its
     292         * completion. This is necessary to make sure that when this method
     293         * returns, the object state is well-defined (NotReady). */
     294
     295        /* lazy semaphore creation */
     296        if (mInitUninitSem == NIL_RTSEMEVENTMULTI)
     297        {
     298            RTSemEventMultiCreate(&mInitUninitSem);
     299            Assert(mInitUninitWaiters == 0);
     300        }
     301        ++mInitUninitWaiters;
     302
     303        LogFlowFunc(("{%p}: Waiting for AutoUninitSpan to finish...\n", mObj));
     304
     305        stateLock.release();
     306        RTSemEventMultiWait(mInitUninitSem, RT_INDEFINITE_WAIT);
     307        stateLock.acquire();
     308
     309        if (--mInitUninitWaiters == 0)
     310        {
     311            /* destroy the semaphore since no more necessary */
     312            RTSemEventMultiDestroy(mInitUninitSem);
     313            mInitUninitSem = NIL_RTSEMEVENTMULTI;
     314        }
     315
     316        /* the other thread set it to NotReady */
     317        return mState;
     318    }
     319
     320    /* go to InUninit to prevent from adding new callers */
     321    setState(InUninit);
     322
     323    /* wait for already existing callers to drop to zero */
     324    if (mCallers > 0)
     325    {
     326        /* lazy creation */
     327        Assert(mZeroCallersSem == NIL_RTSEMEVENT);
     328        RTSemEventCreate(&mZeroCallersSem);
     329
     330        /* wait until remaining callers release the object */
     331        LogFlowFunc(("{%p}: Waiting for callers (%d) to drop to zero...\n",
     332                     mObj, mCallers));
     333
     334        stateLock.release();
     335        RTSemEventWait(mZeroCallersSem, RT_INDEFINITE_WAIT);
     336    }
     337    return mState;
     338}
     339
     340void ObjectState::autoUninitSpanDestructor()
     341{
     342    AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
     343
     344    Assert(mState == InUninit);
     345
     346    setState(NotReady);
     347}
     348
     349
     350void ObjectState::setState(ObjectState::State aState)
     351{
     352    Assert(mState != aState);
     353    mState = aState;
     354    mStateChangeThread = RTThreadSelf();
    771355}
    772356
     
    794378      mOk(false)
    795379{
    796     Assert(aObj);
    797 
    798     AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
    799 
    800     mOk = mObj->mState == VirtualBoxBase::NotReady;
    801     AssertReturnVoid (mOk);
    802 
    803     mObj->setState(VirtualBoxBase::InInit);
     380    Assert(mObj);
     381    mOk = mObj->getObjectState().autoInitSpanConstructor(ObjectState::NotReady);
     382    AssertReturnVoid(mOk);
    804383}
    805384
     
    817396        return;
    818397
    819     AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
    820 
    821     Assert(mObj->mState == VirtualBoxBase::InInit);
    822 
    823     if (mObj->mCallers > 0)
    824     {
    825         Assert(mObj->mInitUninitWaiters > 0);
    826 
    827         /* We have some pending addCaller() calls on other threads (created
    828          * during InInit), signal that InInit is finished and they may go on. */
    829         RTSemEventMultiSignal(mObj->mInitUninitSem);
    830     }
    831 
     398    ObjectState::State newState;
    832399    if (mResult == Succeeded)
    833     {
    834         mObj->setState(VirtualBoxBase::Ready);
    835     }
     400        newState = ObjectState::Ready;
     401    else if (mResult == Limited)
     402        newState = ObjectState::Limited;
    836403    else
    837     if (mResult == Limited)
    838     {
    839         mObj->setState(VirtualBoxBase::Limited);
    840     }
    841     else
    842     {
    843         mObj->setState(VirtualBoxBase::InitFailed);
    844         /* release the lock to prevent nesting when uninit() is called */
    845         stateLock.release();
     404        newState = ObjectState::InitFailed;
     405    mObj->getObjectState().autoInitSpanDestructor(newState);
     406    if (newState == ObjectState::InitFailed)
     407    {
    846408        /* call uninit() to let the object uninit itself after failed init() */
    847409        mObj->uninit();
     
    868430      mOk(false)
    869431{
    870     Assert(aObj);
    871 
    872     AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
    873 
    874     mOk = mObj->mState == VirtualBoxBase::Limited;
    875     AssertReturnVoid (mOk);
    876 
    877     mObj->setState(VirtualBoxBase::InInit);
     432    Assert(mObj);
     433    mOk = mObj->getObjectState().autoInitSpanConstructor(ObjectState::Limited);
     434    AssertReturnVoid(mOk);
    878435}
    879436
     
    891448        return;
    892449
    893     AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
    894 
    895     Assert(mObj->mState == VirtualBoxBase::InInit);
    896 
    897     if (mObj->mCallers > 0 && mObj->mInitUninitWaiters > 0)
    898     {
    899         /* We have some pending addCaller() calls on other threads (created
    900          * during InInit), signal that InInit is finished and they may go on. */
    901         RTSemEventMultiSignal(mObj->mInitUninitSem);
    902     }
    903 
     450    ObjectState::State newState;
    904451    if (mSucceeded)
    905     {
    906         mObj->setState(VirtualBoxBase::Ready);
    907     }
     452        newState = ObjectState::Ready;
    908453    else
    909     {
    910         mObj->setState(VirtualBoxBase::Limited);
    911     }
     454        newState = ObjectState::Limited;
     455    mObj->getObjectState().autoInitSpanDestructor(newState);
     456    /** @todo r=klaus: this is like the initial init() failure, but in this
     457     * place uninit() is NOT called. Makes only limited sense. */
    912458}
    913459
     
    932478      mUninitDone(false)
    933479{
    934     Assert(aObj);
    935 
    936     AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
    937 
    938     Assert(mObj->mState != VirtualBoxBase::InInit);
    939 
    940     /* Set mUninitDone to |true| if this object is already uninitialized
    941      * (NotReady) or if another AutoUninitSpan is currently active on some
    942      *  other thread (InUninit). */
    943     mUninitDone =    mObj->mState == VirtualBoxBase::NotReady
    944                   || mObj->mState == VirtualBoxBase::InUninit;
    945 
    946     if (mObj->mState == VirtualBoxBase::InitFailed)
    947     {
    948         /* we've been called by init() on failure */
     480    Assert(mObj);
     481    ObjectState::State state;
     482    state = mObj->getObjectState().autoUninitSpanConstructor();
     483    if (state == ObjectState::InitFailed)
    949484        mInitFailed = true;
    950     }
    951     else
    952     {
    953         if (mUninitDone)
    954         {
    955             /* do nothing if already uninitialized */
    956             if (mObj->mState == VirtualBoxBase::NotReady)
    957                 return;
    958 
    959             /* otherwise, wait until another thread finishes uninitialization.
    960              * This is necessary to make sure that when this method returns, the
    961              * object is NotReady and therefore can be deleted (for example). */
    962 
    963             /* lazy semaphore creation */
    964             if (mObj->mInitUninitSem == NIL_RTSEMEVENTMULTI)
    965             {
    966                 RTSemEventMultiCreate(&mObj->mInitUninitSem);
    967                 Assert(mObj->mInitUninitWaiters == 0);
    968             }
    969             ++mObj->mInitUninitWaiters;
    970 
    971             LogFlowFunc(("{%p}: Waiting for AutoUninitSpan to finish...\n",
    972                          mObj));
    973 
    974             stateLock.release();
    975             RTSemEventMultiWait(mObj->mInitUninitSem, RT_INDEFINITE_WAIT);
    976             stateLock.acquire();
    977 
    978             if (--mObj->mInitUninitWaiters == 0)
    979             {
    980                 /* destroy the semaphore since no more necessary */
    981                 RTSemEventMultiDestroy(mObj->mInitUninitSem);
    982                 mObj->mInitUninitSem = NIL_RTSEMEVENTMULTI;
    983             }
    984 
    985             return;
    986         }
    987     }
    988 
    989     /* go to InUninit to prevent from adding new callers */
    990     mObj->setState(VirtualBoxBase::InUninit);
    991 
    992     /* wait for already existing callers to drop to zero */
    993     if (mObj->mCallers > 0)
    994     {
    995         /* lazy creation */
    996         Assert(mObj->mZeroCallersSem == NIL_RTSEMEVENT);
    997         RTSemEventCreate(&mObj->mZeroCallersSem);
    998 
    999         /* wait until remaining callers release the object */
    1000         LogFlowFunc(("{%p}: Waiting for callers (%d) to drop to zero...\n",
    1001                      mObj, mObj->mCallers));
    1002 
    1003         stateLock.release();
    1004         RTSemEventWait(mObj->mZeroCallersSem, RT_INDEFINITE_WAIT);
    1005     }
     485    else if (state == ObjectState::NotReady)
     486        mUninitDone = true;
    1006487}
    1007488
     
    1015496        return;
    1016497
    1017     AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
    1018 
    1019     Assert(mObj->mState == VirtualBoxBase::InUninit);
    1020 
    1021     mObj->setState(VirtualBoxBase::NotReady);
    1022 }
    1023 
    1024 ////////////////////////////////////////////////////////////////////////////////
    1025 //
    1026 // MultiResult methods
    1027 //
    1028 ////////////////////////////////////////////////////////////////////////////////
    1029 
    1030 RTTLS MultiResult::sCounter = NIL_RTTLS;
    1031 
    1032 /*static*/
    1033 void MultiResult::incCounter()
    1034 {
    1035     if (sCounter == NIL_RTTLS)
    1036     {
    1037         sCounter = RTTlsAlloc();
    1038         AssertReturnVoid(sCounter != NIL_RTTLS);
    1039     }
    1040 
    1041     uintptr_t counter = (uintptr_t)RTTlsGet(sCounter);
    1042     ++counter;
    1043     RTTlsSet(sCounter, (void*)counter);
    1044 }
    1045 
    1046 /*static*/
    1047 void MultiResult::decCounter()
    1048 {
    1049     uintptr_t counter = (uintptr_t)RTTlsGet(sCounter);
    1050     AssertReturnVoid(counter != 0);
    1051     --counter;
    1052     RTTlsSet(sCounter, (void*)counter);
    1053 }
    1054 
    1055 /*static*/
    1056 bool MultiResult::isMultiEnabled()
    1057 {
    1058     if (sCounter == NIL_RTTLS)
    1059        return false;
    1060 
    1061     return ((uintptr_t)RTTlsGet(MultiResult::sCounter)) > 0;
    1062 }
    1063 
     498    mObj->getObjectState().autoUninitSpanDestructor();
     499}
  • trunk/src/VBox/Main/src-all/VirtualBoxBase.cpp

    r41214 r51903  
    77
    88/*
    9  * Copyright (C) 2006-2012 Oracle Corporation
     9 * Copyright (C) 2006-2014 Oracle Corporation
    1010 *
    1111 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    2424#include <typeinfo>
    2525
    26 #if !defined (VBOX_WITH_XPCOM)
     26#if !defined(VBOX_WITH_XPCOM)
    2727#include <windows.h>
    2828#include <dbghelp.h>
    29 #else /* !defined (VBOX_WITH_XPCOM) */
     29#else /* !defined(VBOX_WITH_XPCOM) */
    3030/// @todo remove when VirtualBoxErrorInfo goes away from here
    3131#include <nsIServiceManager.h>
    3232#include <nsIExceptionService.h>
    33 #endif /* !defined (VBOX_WITH_XPCOM) */
     33#endif /* !defined(VBOX_WITH_XPCOM) */
    3434
    3535#include "VirtualBoxBase.h"
     
    4747////////////////////////////////////////////////////////////////////////////////
    4848
    49 VirtualBoxBase::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;
     49VirtualBoxBase::VirtualBoxBase() : mState(this)
     50{
    5851    mObjectLock = NULL;
    5952}
     
    6356    if (mObjectLock)
    6457        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;
    7258}
    7359
     
    9076    if (RT_UNLIKELY(!mObjectLock))
    9177    {
    92         AssertCompile (sizeof (RWLockHandle *) == sizeof (void *));
     78        AssertCompile(sizeof(RWLockHandle *) == sizeof(void *));
    9379
    9480        // getLockingClass() is overridden by many subclasses to return
     
    10389    }
    10490    return mObjectLock;
    105 }
    106 
    107 /**
    108  * Increments the number of calls to this object by one.
    109  *
    110  * After this method succeeds, it is guaranteed 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 succeeds (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  *       succeed 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  * When this method returns a failure, the caller must not use the object
    151  * and should return the failed result code to its own caller.
    152  *
    153  * @param aState        Where to store the current object's state (can be
    154  *                      used in overridden methods to determine the cause of
    155  *                      the failure).
    156  * @param aLimited      |true| to add a limited caller.
    157  *
    158  * @return              S_OK on success or E_ACCESSDENIED on failure.
    159  *
    160  * @note It is preferable to use the #addLimitedCaller() rather than
    161  *       calling this method with @a aLimited = |true|, for better
    162  *       self-descriptiveness.
    163  *
    164  * @sa #addLimitedCaller()
    165  * @sa #releaseCaller()
    166  */
    167 HRESULT VirtualBoxBase::addCaller(State *aState /* = NULL */,
    168                                   bool aLimited /* = false */)
    169 {
    170     AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
    171 
    172     HRESULT rc = E_ACCESSDENIED;
    173 
    174     if (mState == Ready || (aLimited && mState == Limited))
    175     {
    176         /* if Ready or allows Limited, increase the number of callers */
    177         ++ mCallers;
    178         rc = S_OK;
    179     }
    180     else
    181     if (mState == InInit || mState == InUninit)
    182     {
    183         if (mStateChangeThread == RTThreadSelf())
    184         {
    185             /* Called from the same thread that is doing AutoInitSpan or
    186              * AutoUninitSpan, just succeed */
    187             rc = S_OK;
    188         }
    189         else if (mState == InInit)
    190         {
    191             /* addCaller() is called by a "child" thread while the "parent"
    192              * thread is still doing AutoInitSpan/AutoReinitSpan, so wait for
    193              * the state to become either Ready/Limited or InitFailed (in
    194              * case of init failure).
    195              *
    196              * Note that we increase the number of callers anyway -- to
    197              * prevent AutoUninitSpan from early completion if we are
    198              * still not scheduled to pick up the posted semaphore when
    199              * uninit() is called.
    200              */
    201             ++ mCallers;
    202 
    203             /* lazy semaphore creation */
    204             if (mInitUninitSem == NIL_RTSEMEVENTMULTI)
    205             {
    206                 RTSemEventMultiCreate (&mInitUninitSem);
    207                 Assert(mInitUninitWaiters == 0);
    208             }
    209 
    210             ++ mInitUninitWaiters;
    211 
    212             LogFlowThisFunc(("Waiting for AutoInitSpan/AutoReinitSpan to finish...\n"));
    213 
    214             stateLock.release();
    215             RTSemEventMultiWait (mInitUninitSem, RT_INDEFINITE_WAIT);
    216             stateLock.acquire();
    217 
    218             if (-- mInitUninitWaiters == 0)
    219             {
    220                 /* destroy the semaphore since no more necessary */
    221                 RTSemEventMultiDestroy (mInitUninitSem);
    222                 mInitUninitSem = NIL_RTSEMEVENTMULTI;
    223             }
    224 
    225             if (mState == Ready || (aLimited && mState == Limited))
    226                 rc = S_OK;
    227             else
    228             {
    229                 Assert(mCallers != 0);
    230                 -- mCallers;
    231                 if (mCallers == 0 && mState == InUninit)
    232                 {
    233                     /* inform AutoUninitSpan ctor there are no more callers */
    234                     RTSemEventSignal(mZeroCallersSem);
    235                 }
    236             }
    237         }
    238     }
    239 
    240     if (aState)
    241         *aState = mState;
    242 
    243     if (FAILED(rc))
    244     {
    245         if (mState == VirtualBoxBase::Limited)
    246             rc = setError(rc, "The object functionality is limited");
    247         else
    248             rc = setError(rc, "The object is not ready");
    249     }
    250 
    251     return rc;
    252 }
    253 
    254 /**
    255  * Decreases the number of calls to this object by one.
    256  *
    257  * Must be called after every #addCaller() or #addLimitedCaller() when
    258  * protecting the object from uninitialization is no more necessary.
    259  */
    260 void VirtualBoxBase::releaseCaller()
    261 {
    262     AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
    263 
    264     if (mState == Ready || mState == Limited)
    265     {
    266         /* if Ready or Limited, decrease the number of callers */
    267         AssertMsgReturn(mCallers != 0, ("mCallers is ZERO!"), (void) 0);
    268         --mCallers;
    269 
    270         return;
    271     }
    272 
    273     if (mState == InInit || mState == InUninit)
    274     {
    275         if (mStateChangeThread == RTThreadSelf())
    276         {
    277             /* Called from the same thread that is doing AutoInitSpan or
    278              * AutoUninitSpan: just succeed */
    279             return;
    280         }
    281 
    282         if (mState == InUninit)
    283         {
    284             /* the caller is being released after AutoUninitSpan has begun */
    285             AssertMsgReturn(mCallers != 0, ("mCallers is ZERO!"), (void) 0);
    286             --mCallers;
    287 
    288             if (mCallers == 0)
    289                 /* inform the Auto*UninitSpan ctor there are no more callers */
    290                 RTSemEventSignal(mZeroCallersSem);
    291 
    292             return;
    293         }
    294     }
    295 
    296     AssertMsgFailed (("mState = %d!", mState));
    29791}
    29892
     
    437231        if (FAILED(rc)) break;
    438232
    439 #if !defined (VBOX_WITH_XPCOM)
     233#if !defined(VBOX_WITH_XPCOM)
    440234
    441235        ComPtr<IVirtualBoxErrorInfo> curInfo;
     
    444238            /* get the current error info if any */
    445239            ComPtr<IErrorInfo> err;
    446             rc = ::GetErrorInfo (0, err.asOutParam());
     240            rc = ::GetErrorInfo(0, err.asOutParam());
    447241            if (FAILED(rc)) break;
    448242            rc = err.queryInterfaceTo(curInfo.asOutParam());
     
    455249                if (SUCCEEDED(rc))
    456250                {
    457                     rc = wrapper->init (err);
     251                    rc = wrapper->init(err);
    458252                    if (SUCCEEDED(rc))
    459253                        curInfo = wrapper;
     
    471265        rc = info.queryInterfaceTo(err.asOutParam());
    472266        if (SUCCEEDED(rc))
    473             rc = ::SetErrorInfo (0, err);
    474 
    475 #else // !defined (VBOX_WITH_XPCOM)
     267            rc = ::SetErrorInfo(0, err);
     268
     269#else // !defined(VBOX_WITH_XPCOM)
    476270
    477271        nsCOMPtr <nsIExceptionService> es;
    478         es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
     272        es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
    479273        if (NS_SUCCEEDED(rc))
    480274        {
    481275            nsCOMPtr <nsIExceptionManager> em;
    482             rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
     276            rc = es->GetCurrentExceptionManager(getter_AddRefs(em));
    483277            if (FAILED(rc)) break;
    484278
     
    488282                /* get the current error info if any */
    489283                ComPtr<nsIException> ex;
    490                 rc = em->GetCurrentException (ex.asOutParam());
     284                rc = em->GetCurrentException(ex.asOutParam());
    491285                if (FAILED(rc)) break;
    492286                rc = ex.queryInterfaceTo(curInfo.asOutParam());
     
    499293                    if (SUCCEEDED(rc))
    500294                    {
    501                         rc = wrapper->init (ex);
     295                        rc = wrapper->init(ex);
    502296                        if (SUCCEEDED(rc))
    503297                            curInfo = wrapper;
     
    515309            rc = info.queryInterfaceTo(ex.asOutParam());
    516310            if (SUCCEEDED(rc))
    517                 rc = em->SetCurrentException (ex);
     311                rc = em->SetCurrentException(ex);
    518312        }
    519313        else if (rc == NS_ERROR_UNEXPECTED)
     
    533327        }
    534328
    535 #endif // !defined (VBOX_WITH_XPCOM)
     329#endif // !defined(VBOX_WITH_XPCOM)
    536330    }
    537331    while (0);
    538332
    539     AssertComRC (rc);
     333    AssertComRC(rc);
    540334
    541335    return SUCCEEDED(rc) ? aResultCode : rc;
     
    604398        if (FAILED(rc)) break;
    605399
    606 #if !defined (VBOX_WITH_XPCOM)
     400#if !defined(VBOX_WITH_XPCOM)
    607401
    608402        ComPtr<IVirtualBoxErrorInfo> curInfo;
     
    611405            /* get the current error info if any */
    612406            ComPtr<IErrorInfo> err;
    613             rc = ::GetErrorInfo (0, err.asOutParam());
     407            rc = ::GetErrorInfo(0, err.asOutParam());
    614408            if (FAILED(rc)) break;
    615409            rc = err.queryInterfaceTo(curInfo.asOutParam());
     
    622416                if (SUCCEEDED(rc))
    623417                {
    624                     rc = wrapper->init (err);
     418                    rc = wrapper->init(err);
    625419                    if (SUCCEEDED(rc))
    626420                        curInfo = wrapper;
     
    638432        rc = info.queryInterfaceTo(err.asOutParam());
    639433        if (SUCCEEDED(rc))
    640             rc = ::SetErrorInfo (0, err);
    641 
    642 #else // !defined (VBOX_WITH_XPCOM)
     434            rc = ::SetErrorInfo(0, err);
     435
     436#else // !defined(VBOX_WITH_XPCOM)
    643437
    644438        nsCOMPtr <nsIExceptionService> es;
    645         es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
     439        es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
    646440        if (NS_SUCCEEDED(rc))
    647441        {
    648442            nsCOMPtr <nsIExceptionManager> em;
    649             rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
     443            rc = es->GetCurrentExceptionManager(getter_AddRefs(em));
    650444            if (FAILED(rc)) break;
    651445
     
    655449                /* get the current error info if any */
    656450                ComPtr<nsIException> ex;
    657                 rc = em->GetCurrentException (ex.asOutParam());
     451                rc = em->GetCurrentException(ex.asOutParam());
    658452                if (FAILED(rc)) break;
    659453                rc = ex.queryInterfaceTo(curInfo.asOutParam());
     
    666460                    if (SUCCEEDED(rc))
    667461                    {
    668                         rc = wrapper->init (ex);
     462                        rc = wrapper->init(ex);
    669463                        if (SUCCEEDED(rc))
    670464                            curInfo = wrapper;
     
    682476            rc = info.queryInterfaceTo(ex.asOutParam());
    683477            if (SUCCEEDED(rc))
    684                 rc = em->SetCurrentException (ex);
     478                rc = em->SetCurrentException(ex);
    685479        }
    686480        else if (rc == NS_ERROR_UNEXPECTED)
     
    700494        }
    701495
    702 #endif // !defined (VBOX_WITH_XPCOM)
     496#endif // !defined(VBOX_WITH_XPCOM)
    703497    }
    704498    while (0);
    705499
    706     AssertComRC (rc);
     500    AssertComRC(rc);
    707501
    708502    return SUCCEEDED(rc) ? ei.getResultCode() : rc;
     
    756550{
    757551#if !defined(VBOX_WITH_XPCOM)
    758     ::SetErrorInfo (0, NULL);
     552    ::SetErrorInfo(0, NULL);
    759553#else
    760554    HRESULT rc = S_OK;
     
    764558    {
    765559        nsCOMPtr <nsIExceptionManager> em;
    766         rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
     560        rc = es->GetCurrentExceptionManager(getter_AddRefs(em));
    767561        if (SUCCEEDED(rc))
    768562            em->SetCurrentException(NULL);
     
    771565}
    772566
    773 
    774 ////////////////////////////////////////////////////////////////////////////////
    775 //
    776 // AutoInitSpan methods
    777 //
    778 ////////////////////////////////////////////////////////////////////////////////
    779 
    780 /**
    781  * Creates a smart initialization span object that places the object to
    782  * InInit state.
    783  *
    784  * Please see the AutoInitSpan class description for more info.
    785  *
    786  * @param aObj      |this| pointer of the managed VirtualBoxBase object whose
    787  *                  init() method is being called.
    788  * @param aResult   Default initialization result.
    789  */
    790 AutoInitSpan::AutoInitSpan(VirtualBoxBase *aObj,
    791                            Result aResult /* = Failed */)
    792     : mObj(aObj),
    793       mResult(aResult),
    794       mOk(false)
    795 {
    796     Assert(aObj);
    797 
    798     AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
    799 
    800     mOk = mObj->mState == VirtualBoxBase::NotReady;
    801     AssertReturnVoid (mOk);
    802 
    803     mObj->setState(VirtualBoxBase::InInit);
    804 }
    805 
    806 /**
    807  * Places the managed VirtualBoxBase object to Ready/Limited state if the
    808  * initialization succeeded or partly succeeded, or places it to InitFailed
    809  * state and calls the object's uninit() method.
    810  *
    811  * Please see the AutoInitSpan class description for more info.
    812  */
    813 AutoInitSpan::~AutoInitSpan()
    814 {
    815     /* if the state was other than NotReady, do nothing */
    816     if (!mOk)
    817         return;
    818 
    819     AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
    820 
    821     Assert(mObj->mState == VirtualBoxBase::InInit);
    822 
    823     if (mObj->mCallers > 0)
    824     {
    825         Assert(mObj->mInitUninitWaiters > 0);
    826 
    827         /* We have some pending addCaller() calls on other threads (created
    828          * during InInit), signal that InInit is finished and they may go on. */
    829         RTSemEventMultiSignal(mObj->mInitUninitSem);
    830     }
    831 
    832     if (mResult == Succeeded)
    833     {
    834         mObj->setState(VirtualBoxBase::Ready);
    835     }
    836     else
    837     if (mResult == Limited)
    838     {
    839         mObj->setState(VirtualBoxBase::Limited);
    840     }
    841     else
    842     {
    843         mObj->setState(VirtualBoxBase::InitFailed);
    844         /* release the lock to prevent nesting when uninit() is called */
    845         stateLock.release();
    846         /* call uninit() to let the object uninit itself after failed init() */
    847         mObj->uninit();
    848         /* Note: the object may no longer exist here (for example, it can call
    849          * the destructor in uninit()) */
    850     }
    851 }
    852 
    853 // AutoReinitSpan methods
    854 ////////////////////////////////////////////////////////////////////////////////
    855 
    856 /**
    857  * Creates a smart re-initialization span object and places the object to
    858  * InInit state.
    859  *
    860  * Please see the AutoInitSpan class description for more info.
    861  *
    862  * @param aObj      |this| pointer of the managed VirtualBoxBase object whose
    863  *                  re-initialization method is being called.
    864  */
    865 AutoReinitSpan::AutoReinitSpan(VirtualBoxBase *aObj)
    866     : mObj(aObj),
    867       mSucceeded(false),
    868       mOk(false)
    869 {
    870     Assert(aObj);
    871 
    872     AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
    873 
    874     mOk = mObj->mState == VirtualBoxBase::Limited;
    875     AssertReturnVoid (mOk);
    876 
    877     mObj->setState(VirtualBoxBase::InInit);
    878 }
    879 
    880 /**
    881  * Places the managed VirtualBoxBase object to Ready state if the
    882  * re-initialization succeeded (i.e. #setSucceeded() has been called) or back to
    883  * Limited state otherwise.
    884  *
    885  * Please see the AutoInitSpan class description for more info.
    886  */
    887 AutoReinitSpan::~AutoReinitSpan()
    888 {
    889     /* if the state was other than Limited, do nothing */
    890     if (!mOk)
    891         return;
    892 
    893     AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
    894 
    895     Assert(mObj->mState == VirtualBoxBase::InInit);
    896 
    897     if (mObj->mCallers > 0 && mObj->mInitUninitWaiters > 0)
    898     {
    899         /* We have some pending addCaller() calls on other threads (created
    900          * during InInit), signal that InInit is finished and they may go on. */
    901         RTSemEventMultiSignal(mObj->mInitUninitSem);
    902     }
    903 
    904     if (mSucceeded)
    905     {
    906         mObj->setState(VirtualBoxBase::Ready);
    907     }
    908     else
    909     {
    910         mObj->setState(VirtualBoxBase::Limited);
    911     }
    912 }
    913 
    914 // AutoUninitSpan methods
    915 ////////////////////////////////////////////////////////////////////////////////
    916 
    917 /**
    918  * Creates a smart uninitialization span object and places this object to
    919  * InUninit state.
    920  *
    921  * Please see the AutoInitSpan class description for more info.
    922  *
    923  * @note This method blocks the current thread execution until the number of
    924  *       callers of the managed VirtualBoxBase object drops to zero!
    925  *
    926  * @param aObj  |this| pointer of the VirtualBoxBase object whose uninit()
    927  *              method is being called.
    928  */
    929 AutoUninitSpan::AutoUninitSpan(VirtualBoxBase *aObj)
    930     : mObj(aObj),
    931       mInitFailed(false),
    932       mUninitDone(false)
    933 {
    934     Assert(aObj);
    935 
    936     AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
    937 
    938     Assert(mObj->mState != VirtualBoxBase::InInit);
    939 
    940     /* Set mUninitDone to |true| if this object is already uninitialized
    941      * (NotReady) or if another AutoUninitSpan is currently active on some
    942      *  other thread (InUninit). */
    943     mUninitDone =    mObj->mState == VirtualBoxBase::NotReady
    944                   || mObj->mState == VirtualBoxBase::InUninit;
    945 
    946     if (mObj->mState == VirtualBoxBase::InitFailed)
    947     {
    948         /* we've been called by init() on failure */
    949         mInitFailed = true;
    950     }
    951     else
    952     {
    953         if (mUninitDone)
    954         {
    955             /* do nothing if already uninitialized */
    956             if (mObj->mState == VirtualBoxBase::NotReady)
    957                 return;
    958 
    959             /* otherwise, wait until another thread finishes uninitialization.
    960              * This is necessary to make sure that when this method returns, the
    961              * object is NotReady and therefore can be deleted (for example). */
    962 
    963             /* lazy semaphore creation */
    964             if (mObj->mInitUninitSem == NIL_RTSEMEVENTMULTI)
    965             {
    966                 RTSemEventMultiCreate(&mObj->mInitUninitSem);
    967                 Assert(mObj->mInitUninitWaiters == 0);
    968             }
    969             ++mObj->mInitUninitWaiters;
    970 
    971             LogFlowFunc(("{%p}: Waiting for AutoUninitSpan to finish...\n",
    972                          mObj));
    973 
    974             stateLock.release();
    975             RTSemEventMultiWait(mObj->mInitUninitSem, RT_INDEFINITE_WAIT);
    976             stateLock.acquire();
    977 
    978             if (--mObj->mInitUninitWaiters == 0)
    979             {
    980                 /* destroy the semaphore since no more necessary */
    981                 RTSemEventMultiDestroy(mObj->mInitUninitSem);
    982                 mObj->mInitUninitSem = NIL_RTSEMEVENTMULTI;
    983             }
    984 
    985             return;
    986         }
    987     }
    988 
    989     /* go to InUninit to prevent from adding new callers */
    990     mObj->setState(VirtualBoxBase::InUninit);
    991 
    992     /* wait for already existing callers to drop to zero */
    993     if (mObj->mCallers > 0)
    994     {
    995         /* lazy creation */
    996         Assert(mObj->mZeroCallersSem == NIL_RTSEMEVENT);
    997         RTSemEventCreate(&mObj->mZeroCallersSem);
    998 
    999         /* wait until remaining callers release the object */
    1000         LogFlowFunc(("{%p}: Waiting for callers (%d) to drop to zero...\n",
    1001                      mObj, mObj->mCallers));
    1002 
    1003         stateLock.release();
    1004         RTSemEventWait(mObj->mZeroCallersSem, RT_INDEFINITE_WAIT);
    1005     }
    1006 }
    1007 
    1008 /**
    1009  *  Places the managed VirtualBoxBase object to the NotReady state.
    1010  */
    1011 AutoUninitSpan::~AutoUninitSpan()
    1012 {
    1013     /* do nothing if already uninitialized */
    1014     if (mUninitDone)
    1015         return;
    1016 
    1017     AutoWriteLock stateLock(mObj->mStateLock COMMA_LOCKVAL_SRC_POS);
    1018 
    1019     Assert(mObj->mState == VirtualBoxBase::InUninit);
    1020 
    1021     mObj->setState(VirtualBoxBase::NotReady);
    1022 }
    1023567
    1024568////////////////////////////////////////////////////////////////////////////////
  • trunk/src/VBox/Main/src-client/ConsoleImpl.cpp

    r51765 r51903  
    71777177
    71787178    LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
    7179             Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
     7179            Global::stringifyMachineState(mMachineState), getObjectState().getState() == ObjectState::InUninit));
    71807180
    71817181    /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
     
    73207320    /* If we are called from Console::uninit(), then try to destroy the VM even
    73217321     * on failure (this will most likely fail too, but what to do?..) */
    7322     if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
     7322    if (RT_SUCCESS(vrc) || getObjectState().getState() == ObjectState::InUninit)
    73237323    {
    73247324        /* If the machine has a USB controller, release all USB devices
     
    75187518{
    75197519    /* sanity check */
    7520     AssertReturn(AutoCaller(this).state() == InInit ||
    7521                  isWriteLockOnCurrentThread(), E_FAIL);
     7520    AssertReturn(   getObjectState().getState() == ObjectState::InInit
     7521                 || isWriteLockOnCurrentThread(), E_FAIL);
    75227522
    75237523    LogFlowThisFunc(("Entering\n"));
     
    78637863     * 2) VM-(guest-)initiated power off. */
    78647864    AssertReturnVoid(   autoCaller.isOk()
    7865                      || autoCaller.state() == InUninit);
     7865                     || that->getObjectState().getState() == ObjectState::InUninit);
    78667866
    78677867    switch (enmState)
  • trunk/src/VBox/Main/src-client/SessionImpl.cpp

    r51612 r51903  
    475475    AutoCaller autoCaller(this);
    476476
    477     if (autoCaller.state() != Ready)
     477    if (getObjectState().getState() != ObjectState::Ready)
    478478    {
    479479        /*
     
    514514    HRESULT rc = S_OK;
    515515
    516     if (autoCaller.state() == Ready)
     516    if (getObjectState().getState() == ObjectState::Ready)
    517517    {
    518518        /* close() needs write lock */
     
    536536        rc = unlockMachine(false /* aFinalRelease */, true /* aFromServer */);
    537537    }
    538     else if (autoCaller.state() == InUninit)
     538    else if (getObjectState().getState() == ObjectState::InUninit)
    539539    {
    540540        /*
  • trunk/src/VBox/Main/src-server/HostImpl.cpp

    r51092 r51903  
    55
    66/*
    7  * Copyright (C) 2004-2013 Oracle Corporation
     7 * Copyright (C) 2004-2014 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    29722972{
    29732973#ifdef VBOX_WITH_HOSTNETIF_API
    2974     AssertReturn(AutoCaller(this).state() == InInit ||
    2975                  isWriteLockOnCurrentThread(), E_FAIL);
     2974    AssertReturn(   getObjectState().getState() == ObjectState::InInit
     2975                 || isWriteLockOnCurrentThread(), E_FAIL);
    29762976
    29772977    HostNetworkInterfaceList list, listCopy;
  • trunk/src/VBox/Main/src-server/MachineImpl.cpp

    r51770 r51903  
    74367436
    74377437    /* just return false for inaccessible machines */
    7438     if (autoCaller.state() != Ready)
     7438    if (getObjectState().getState() != ObjectState::Ready)
    74397439        return false;
    74407440
     
    74697469
    74707470    /* just return false for inaccessible machines */
    7471     if (autoCaller.state() != Ready)
     7471    if (getObjectState().getState() != ObjectState::Ready)
    74727472        return false;
    74737473
     
    75997599                        mData->mUuid.toString().c_str());
    76007600
    7601     AssertReturn(autoCaller.state() == Ready, E_FAIL);
     7601    AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
    76027602
    76037603    if (mData->mRegistered)
     
    78537853    AutoCaller autoCaller(this);
    78547854    AssertComRCReturnRC(autoCaller.rc());
    7855     AssertComRCReturn(autoCaller.state() == InInit ||
    7856                       autoCaller.state() == Limited, E_FAIL);
     7855    AssertComRCReturn(   getObjectState().getState() == ObjectState::InInit
     7856                      || getObjectState().getState() == ObjectState::Limited, E_FAIL);
    78577857
    78587858    AssertReturn(!mData->mAccessible, E_FAIL);
     
    79277927    AutoCaller autoCaller(this);
    79287928    AssertComRCReturnVoid(autoCaller.rc());
    7929     AssertComRCReturnVoid(    autoCaller.state() == InUninit
    7930                            || autoCaller.state() == Limited);
     7929    AssertComRCReturnVoid(   getObjectState().getState() == ObjectState::InUninit
     7930                          || getObjectState().getState() == ObjectState::Limited);
    79317931
    79327932    /* tell all our other child objects we've been uninitialized */
     
    1271612716    AutoCaller autoCaller(this);
    1271712717
    12718     LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
     12718    LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
    1271912719    /*
    1272012720     *  We don't assert below because it might happen that a non-direct session
  • trunk/src/VBox/Main/src-server/MediumImpl.cpp

    r51888 r51903  
    55
    66/*
    7  * Copyright (C) 2008-2013 Oracle Corporation
     7 * Copyright (C) 2008-2014 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    13751375    {
    13761376        /* remove the caller reference we added in setFormat() */
    1377         m->formatObj->releaseCaller();
     1377        m->formatObj->getObjectState().releaseCaller();
    13781378        m->formatObj.setNull();
    13791379    }
     
    60046004     * no format is known yet */
    60056005    AssertReturn(    (!m->strFormat.isEmpty() && !m->formatObj.isNull())
    6006                   || (    autoCaller.state() == InInit
     6006                  || (    getObjectState().getState() == ObjectState::InInit
    60076007                       && m->state != MediumState_NotCreated
    60086008                       && m->id.isZero()
     
    61776177        /* reference the format permanently to prevent its unexpected
    61786178         * uninitialization */
    6179         HRESULT rc = m->formatObj->addCaller();
     6179        HRESULT rc = m->formatObj->getObjectState().addCaller(m->formatObj);
    61806180        AssertComRCReturnRC(rc);
    61816181
  • trunk/src/VBox/Main/src-server/SnapshotImpl.cpp

    r51498 r51903  
    17971797    AutoCaller autoCaller(this);
    17981798
    1799     LogFlowThisFunc(("state=%d\n", autoCaller.state()));
     1799    LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
    18001800    if (!autoCaller.isOk())
    18011801    {
     
    23382338    AutoCaller autoCaller(this);
    23392339
    2340     LogFlowThisFunc(("state=%d\n", autoCaller.state()));
     2340    LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
    23412341    if (!autoCaller.isOk())
    23422342    {
  • trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp

    r51888 r51903  
    22722272    if (SUCCEEDED((rc = autoCaller.rc())))
    22732273    {
    2274         if (autoCaller.state() != Ready)
     2274        if (getObjectState().getState() != ObjectState::Ready)
    22752275            LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the event is discarded!\n",
    2276                             autoCaller.state()));
     2276                            getObjectState().getState()));
    22772277            // return S_OK
    22782278        else if (    (m->pAsyncEventQ)
     
    41284128    }
    41294129
    4130     if (autoCaller.state() != InInit)
     4130    if (getObjectState().getState() != ObjectState::InInit)
    41314131    {
    41324132        rc = aMachine->i_prepareRegister();
     
    41374137    m->allMachines.addChild(aMachine);
    41384138
    4139     if (autoCaller.state() != InInit)
     4139    if (getObjectState().getState() != ObjectState::InInit)
    41404140        rc = i_saveSettings();
    41414141
     
    45194519                    continue;
    45204520                /* object is already dead, no point in saving settings */
    4521                 if (autoCaller.state() != Ready)
     4521                if (getObjectState().getState() != ObjectState::Ready)
    45224522                    continue;
    45234523                AutoWriteLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
     
    46994699    {
    47004700        LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
    4701                         autoCaller.state()));
     4701                        mVirtualBox->getObjectState().getState()));
    47024702        /* We don't need mVirtualBox any more, so release it */
    47034703        mVirtualBox = NULL;
  • trunk/src/VBox/Main/testcase/Makefile.kmk

    r50613 r51903  
    242242        ../src-client/MouseImpl.cpp \
    243243        ../src-all/EventImpl.cpp \
     244        ../src-all/AutoCaller.cpp \
    244245        ../src-all/VirtualBoxBase.cpp \
    245246        ../src-all/VirtualBoxErrorInfoImpl.cpp \
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette