VirtualBox

Changeset 2804 in vbox for trunk/src


Ignore:
Timestamp:
May 23, 2007 1:44:21 PM (18 years ago)
Author:
vboxsync
Message:

Main: Added Machine::AutoStateDependency smart class that helps to ensure the machine state won't unexpectedly change, in a lock-free manner.

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

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/MachineImpl.cpp

    r2672 r2804  
    124124    RTTIMESPEC time;
    125125    mLastStateChange = RTTimeSpecGetMilli (RTTimeNow (&time));
     126
     127    mMachineStateDeps = 0;
     128    mZeroMachineStateDepsSem = NIL_RTSEMEVENT;
     129    mWaitingStateDeps = FALSE;
     130
    126131    mCurrentStateModified = TRUE;
    127132    mHandleCfgFile = NIL_RTFILE;
     
    133138Machine::Data::~Data()
    134139{
     140    if (mZeroMachineStateDepsSem != NIL_RTSEMEVENT)
     141    {
     142        RTSemEventDestroy (mZeroMachineStateDepsSem);
     143        mZeroMachineStateDepsSem = NIL_RTSEMEVENT;
     144    }
    135145}
    136146
     
    29172927    AutoLock alock (this);
    29182928
     2929    /* wait for state dependants to drop to zero */
     2930    checkStateDependencies (alock);
     2931
    29192932    ComAssertRet (mData->mRegistered != aRegistered, E_FAIL);
    29202933
     
    29963009}
    29973010
     3011/**
     3012 *  Increases the number of objects dependent on the machine state or on the
     3013 *  registered state.  Guarantees that these two states will not change at
     3014 *  least until #releaseStateDependency() is called.
     3015 *
     3016 *  Depending on the @a aDepType value, additional state checks may be
     3017 *  made. These checks will set extended error info on failure.
     3018 *
     3019 *  If this method returns a failure, the dependency is not added and the
     3020 *  caller is not allowed to rely on any particular machine state or
     3021 *  registration state value and may return the failed result code to the
     3022 *  upper level.
     3023 *
     3024 *  @param aDepType     Dependency type to choose
     3025 *  @param aState       Current machine state (NULL if not interested).
     3026 *  @param aRegistered  Current registered state (NULL if not interested).
     3027 */
     3028HRESULT Machine::addStateDependency (StateDependency aDepType /* = AnyStateDep */,
     3029                                     MachineState_T *aState /* = NULL */,
     3030                                     BOOL *aRegistered /* = NULL */)
     3031{
     3032    AutoCaller autoCaller (this);
     3033    AssertComRCReturnRC (autoCaller.rc());
     3034
     3035    AutoLock alock (this);
     3036
     3037    if (mData->mWaitingStateDeps && mData->mMachineStateDeps == 0)
     3038    {
     3039        /* checkStateDependencies() is at the point after RTSemEventWait() but
     3040         * before entering the lock. Report an error. It would be better to
     3041         * leave the lock now and re-schedule ourselves, but we don't have a
     3042         * framework that can guarantee such a behavior in 100% cases. */
     3043
     3044        AssertFailed(); /* <-- this is just to see how often it can happen */
     3045
     3046        return setError (E_ACCESSDENIED,
     3047            tr ("The machine is busy: state transition is in progress. "
     3048                "Retry the operation (state is %d)"),
     3049            mData->mMachineState);
     3050    }
     3051
     3052    switch (aDepType)
     3053    {
     3054        case AnyStateDep:
     3055        {
     3056            break;
     3057        }
     3058        case MutableStateDep:
     3059        {
     3060            if (mData->mRegistered &&
     3061                (mType != IsSessionMachine ||
     3062                 mData->mMachineState > MachineState_Paused ||
     3063                 mData->mMachineState == MachineState_Saved))
     3064                return setError (E_ACCESSDENIED,
     3065                    tr ("The machine is not mutable (state is %d)"),
     3066                    mData->mMachineState);
     3067            break;
     3068        }
     3069        case MutableOrSavedStateDep:
     3070        {
     3071            if (mData->mRegistered &&
     3072                (mType != IsSessionMachine ||
     3073                 mData->mMachineState > MachineState_Paused))
     3074                return setError (E_ACCESSDENIED,
     3075                    tr ("The machine is not mutable (state is %d)"),
     3076                    mData->mMachineState);
     3077            break;
     3078        }
     3079    }
     3080
     3081    if (aState)
     3082        *aState = mData->mMachineState;
     3083    if (aRegistered)
     3084        *aRegistered = mData->mRegistered;
     3085
     3086    ++ mData->mMachineStateDeps;
     3087
     3088    return S_OK;
     3089}
     3090
     3091/**
     3092 *  Decreases the number of objects dependent on the machine state.
     3093 *  Must always complete the #addStateDependency() call after the state
     3094 *  dependency no more necessary.
     3095 */
     3096void Machine::releaseStateDependency()
     3097{
     3098    AutoCaller autoCaller (this);
     3099    AssertComRCReturnVoid (autoCaller.rc());
     3100
     3101    AutoLock alock (this);
     3102
     3103    AssertReturnVoid (mData->mMachineStateDeps > 0);
     3104    -- mData->mMachineStateDeps;
     3105
     3106    if (mData->mMachineStateDeps == 0 &&
     3107        mData->mZeroMachineStateDepsSem != NIL_RTSEMEVENT)
     3108    {
     3109        /* inform checkStateDependencies() that there are no more deps */
     3110        RTSemEventSignal (mData->mZeroMachineStateDepsSem);
     3111    }
     3112}
     3113
    29983114// protected methods
    29993115/////////////////////////////////////////////////////////////////////////////
     
    30773193}
    30783194
     3195
     3196/**
     3197 *  Chhecks that there are no state dependants. If necessary, waits for the
     3198 *  number of dependants to drop to zero. Must be called from under
     3199 *  this object's lock.
     3200 *
     3201 *  @param aLock    This object's lock.
     3202 *
     3203 *  @note This method may leave the object lock during its execution!
     3204 */
     3205void Machine::checkStateDependencies (AutoLock &aLock)
     3206{
     3207    AssertReturnVoid (isLockedOnCurrentThread());
     3208    AssertReturnVoid (aLock.belongsTo (this));
     3209
     3210    /* Wait for all state dependants if necessary */
     3211    if (mData->mMachineStateDeps > 0)
     3212    {
     3213        /* lazy creation */
     3214        if (mData->mZeroMachineStateDepsSem == NIL_RTSEMEVENT)
     3215            RTSemEventCreate (&mData->mZeroMachineStateDepsSem);
     3216
     3217        LogFlowThisFunc (("Waiting for state deps (%d) to drop to zero...\n",
     3218                          mData->mMachineStateDeps));
     3219
     3220        mData->mWaitingStateDeps = TRUE;
     3221
     3222        aLock.leave();
     3223
     3224        RTSemEventWait (mData->mZeroMachineStateDepsSem, RT_INDEFINITE_WAIT);
     3225
     3226        aLock.enter();
     3227
     3228        mData->mWaitingStateDeps = FALSE;
     3229    }
     3230}
     3231
    30793232/**
    30803233 *  Helper to change the machine state.
     
    30913244
    30923245    AutoLock alock (this);
     3246
     3247    /* wait for state dependants to drop to zero */
     3248        /// @todo it may be potentially unsafe to leave the lock here as
     3249    //  the below method does. Needs some thinking. The easiest solution may
     3250    //  be to provide a separate mutex for mMachineState and mRegistered.
     3251    checkStateDependencies (alock);
    30933252
    30943253    if (mData->mMachineState != aMachineState)
     
    33773536            mSSData->mStateFilePath.setNull();
    33783537
     3538            /* no need to use setMachineState() during init() */
    33793539            mData->mMachineState = MachineState_Aborted;
    33803540        }
    33813541        else if (mSSData->mStateFilePath)
    33823542        {
     3543            /* no need to use setMachineState() during init() */
    33833544            mData->mMachineState = MachineState_Saved;
    33843545        }
     
    72917452         *  below, the following is enough.
    72927453         */
    7293         LogFlowThisFunc (("Initialization failed\n"));
     7454        LogFlowThisFunc (("Initialization failed.\n"));
    72947455#if defined(__WIN__)
    72957456        if (mIPCSem)
     
    73157476    AutoMultiLock <2> alock (mParent->wlock(), this->wlock());
    73167477
     7478    MachineState_T lastState = mData->mMachineState;
     7479
     7480    if (aReason == Uninit::Abnormal)
     7481    {
     7482        LogWarningThisFunc (("ABNORMAL client termination! (wasRunning=%d)\n",
     7483                             lastState >= MachineState_Running));
     7484
     7485        /* reset the state to Aborted */
     7486        if (mData->mMachineState != MachineState_Aborted)
     7487            setMachineState (MachineState_Aborted);
     7488    }
     7489
    73177490    if (isModified())
    73187491    {
     
    73347507
    73357508    /* release all captured USB devices */
    7336     mParent->host()->releaseAllUSBDevices (this);
     7509    if (aReason == Uninit::Abnormal && lastState >= MachineState_Running)
     7510    {
     7511        /* Console::captureUSBDevices() is called in the VM process only after
     7512         * setting the machine state to Starting or Restoring.
     7513         * Console::releaseAllUSBDevices() will be called upon successful
     7514         * termination. So, we need to release USB devices only if there was
     7515         * an abnormal termination of a running VM. */
     7516        ReleaseAllUSBDevices();
     7517    }
    73377518
    73387519    if (!mData->mSession.mType.isNull())
     
    75577738
    75587739/**
    7559  *  @note Locks the same as Host::autoCaptureUSBDevices() does.
     7740 *  Inserts all machine filters to the USB proxy service and then calls
     7741 *  Host::autoCaptureUSBDevices().
     7742 *
     7743 *  Called by Console from the VM process upon VM startup.
     7744 *
     7745 *  @note Locks what called methods lock.
    75607746 */
    75617747STDMETHODIMP SessionMachine::AutoCaptureUSBDevices (IUSBDeviceCollection **aHostDevices)
     
    75667752    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
    75677753
     7754    HRESULT rc = mUSBController->notifyProxy (true /* aInsertFilters */);
     7755    AssertComRC (rc);
     7756    NOREF (rc);
     7757
    75687758    return mParent->host()->autoCaptureUSBDevices (this, aHostDevices);
    75697759}
    75707760
    75717761/**
    7572  *  @note Locks the same as Host::releaseAllUSBDevices() does.
     7762 *  Removes all machine filters from the USB proxy service and then calls
     7763 *  Host::releaseAllUSBDevices().
     7764 *
     7765 *  Called by Console from the VM process upon normal VM termination or by
     7766 *  SessionMachine::uninit() upon abnormal VM termination (from under the
     7767 *  Machine/SessionMachine lock).
     7768 *
     7769 *  @note Locks what called methods lock.
    75737770 */
    75747771STDMETHODIMP SessionMachine::ReleaseAllUSBDevices()
     
    75787775    AutoCaller autoCaller (this);
    75797776    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
     7777
     7778    HRESULT rc = mUSBController->notifyProxy (false /* aInsertFilters */);
     7779    AssertComRC (rc);
     7780    NOREF (rc);
    75807781
    75817782    return mParent->host()->releaseAllUSBDevices (this);
     
    82358436        AssertMsg (mIPCSem, ("semaphore must be created"));
    82368437
    8237         if (reason == Uninit::Abnormal)
    8238         {
    8239             LogWarningThisFunc (("ABNORMAL client termination! (wasRunning=%d)\n",
    8240                                  mData->mMachineState >= MachineState_Running));
    8241 
    8242             /* reset the state to Aborted */
    8243             if (mData->mMachineState != MachineState_Aborted)
    8244                 setMachineState (MachineState_Aborted);
    8245         }
    8246 
    82478438        /* release the IPC mutex */
    82488439        ::ReleaseMutex (mIPCSem);
     
    82608451        {
    82618452            /* the semaphore is signaled, meaning the session is terminated */
    8262 
    8263             if (reason == Uninit::Abnormal)
    8264             {
    8265                 LogWarningThisFunc (("ABNORMAL client termination! (wasRunning=%d)\n",
    8266                                      mData->mMachineState >= MachineState_Running));
    8267 
    8268                 /* reset the state to Aborted */
    8269                 if (mData->mMachineState != MachineState_Aborted)
    8270                     setMachineState (MachineState_Aborted);
    8271             }
    8272 
    82738453            doUninit = true;
    82748454        }
  • trunk/src/VBox/Main/include/MachineImpl.h

    r2567 r2804  
    5959 *  @param machine  the machine object (must cast to Machine *)
    6060 */
     61/// @todo replace with AutoStateDependency
    6162#define CHECK_MACHINE_MUTABILITY(machine) \
    6263    do { \
     
    6465            return setError (E_ACCESSDENIED, tr ("The machine is not mutable")); \
    6566    } while (0)
    66 /** like CHECK_MACHINE_MUTABILITY but a saved state is ok, too */
     67
     68/** Like CHECK_MACHINE_MUTABILITY but a saved state is OK, too. */
     69/// @todo replace with AutoStateDependency
    6770#define CHECK_MACHINE_MUTABILITY_IGNORING_SAVED(machine) \
    6871    do { \
    6972        if (!machine->isMutableIgnoringSavedState()) \
    70             return setError (E_ACCESSDENIED, tr ("The machine is not mutable or in saved state")); \
     73            return setError (E_ACCESSDENIED, \
     74                             tr ("The machine is not mutable or in saved state")); \
    7175    } while (0)
    7276
     
    167171        LONG64 mLastStateChange;
    168172
     173        uint32_t mMachineStateDeps;
     174        RTSEMEVENT mZeroMachineStateDepsSem;
     175        BOOL mWaitingStateDeps;
     176
    169177        BOOL mCurrentStateModified;
    170178
     
    277285         */
    278286        bool mHDAttachmentsChanged;
     287    };
     288
     289    enum StateDependency
     290    {
     291        AnyStateDep = 0, MutableStateDep, MutableOrSavedStateDep
     292    };
     293
     294    /**
     295     *  Helper class that safely manages the machine state dependency by
     296     *  calling Machine::addStateDependency() on construction and
     297     *  Machine::releaseStateDependency() on destruction. Intended for Machine
     298     *  children. The usage pattern is:
     299     *
     300     *  @code
     301     *      Machine::AutoStateDependency <MutableStateDep> adep (mParent);
     302     *      CheckComRCReturnRC (stateDep.rc());
     303     *      ...
     304     *      // code that depends on the particular machine state
     305     *      ...
     306     *  @endcode
     307     *
     308     *  @param taDepType    Dependecy type to manage.
     309     */
     310    template <StateDependency taDepType = AnyStateDep>
     311    class AutoStateDependency
     312    {
     313    public:
     314
     315        AutoStateDependency (Machine *aThat)
     316            : mThat (aThat), mRC (S_OK)
     317            , mMachineState (MachineState_InvalidMachineState)
     318            , mRegistered (FALSE)
     319        {
     320            Assert (aThat);
     321            mRC = aThat->addStateDependency (taDepType, &mMachineState,
     322                                             &mRegistered);
     323        }
     324        ~AutoStateDependency()
     325        {
     326            if (SUCCEEDED (mRC))
     327                mThat->releaseStateDependency();
     328        }
     329
     330        /** Decreases the number of dependencies before the instance is
     331         *  destroyed. Note that will reset #rc() to E_FAIL. */
     332        void release()
     333        {
     334            AssertReturnVoid (SUCCEEDED (mRC));
     335            mThat->releaseStateDependency();
     336            mRC = E_FAIL;
     337        }
     338
     339        /** Restores the number of callers after by #release(). #rc() will be
     340         *  reset to the result of calling addStateDependency() and must be
     341         *  rechecked to ensure the operation succeeded. */
     342        void add()
     343        {
     344            AssertReturnVoid (!SUCCEEDED (mRC));
     345            mRC = mThat->addStateDependency (taDepType, &mMachineState,
     346                                             &mRegistered);
     347        }
     348
     349        /** Returns the result of Machine::addStateDependency(). */
     350        HRESULT rc() const { return mRC; }
     351
     352        /** Shortcut to SUCCEEDED (rc()). */
     353        bool isOk() const { return SUCCEEDED (mRC); }
     354
     355        /** Returns the machine state value as returned by
     356         *  Machine::addStateDependency(). */
     357        MachineState_T machineState() const { return mMachineState; }
     358
     359        /** Returns the machine state value as returned by
     360         *  Machine::addStateDependency(). */
     361        BOOL machineRegistered() const { return mRegistered; }
     362
     363    protected:
     364
     365        Machine *mThat;
     366        HRESULT mRC;
     367        MachineState_T mMachineState;
     368        BOOL mRegistered;
     369
     370    private:
     371
     372        DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoStateDependency)
     373        DECLARE_CLS_NEW_DELETE_NOOP (AutoStateDependency)
    279374    };
    280375
     
    375470    //  Note: these classes should enter Machine lock to keep the returned
    376471    //  information valid!
     472    /// @todo replace with AutoStateDependency
    377473    bool isMutable()
    378474    {
     
    387483    //  Note: these classes should enter Machine lock to keep the returned
    388484    //  information valid!
     485    /// @todo replace with AutoStateDependency
    389486    bool isMutableIgnoringSavedState()
    390487    {
     
    446543    }
    447544
     545    HRESULT addStateDependency (StateDependency aDepType = AnyStateDep,
     546                                MachineState_T *aState = NULL,
     547                                BOOL *aRegistered = NULL);
     548    void releaseStateDependency();
     549
    448550    // for VirtualBoxSupportErrorInfoImpl
    449551    static const wchar_t *getComponentName() { return L"Machine"; }
     
    458560
    459561    void uninitDataAndChildObjects();
     562
     563    void checkStateDependencies (AutoLock &aLock);
    460564
    461565    virtual HRESULT setMachineState (MachineState_T aMachineState);
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