Logo Search packages:      
Sourcecode: virtualbox-ose version File versions

HRESULT VirtualBoxBaseProto::addCaller ( State *  aState = NULL,
bool  aLimited = false 
) [virtual, inherited]

Increments the number of calls to this object by one.

After this method succeeds, it is guaranted that the object will remain in the Ready (or in the Limited) state at least until releaseCaller() is called.

This method is intended to mark the beginning of sections of code within methods of COM objects that depend on the readiness (Ready) state. The Ready state is a primary "ready to serve" state. Usually all code that works with component's data depends on it. On practice, this means that almost every public method, setter or getter of the object should add itself as an object's caller at the very beginning, to protect from an unexpected uninitialization that may happen on a different thread.

Besides the Ready state denoting that the object is fully functional, there is a special Limited state. The Limited state means that the object is still functional, but its functionality is limited to some degree, so not all operations are possible. The aLimited argument to this method determines whether the caller represents this limited functionality or not.

This method succeeeds (and increments the number of callers) only if the current object's state is Ready. Otherwise, it will return E_ACCESSDENIED to indicate that the object is not operational. There are two exceptions from this rule:

  1. If the aLimited argument is |true|, then this method will also succeeed if the object's state is Limited (or Ready, of course).
  2. If this method is called from the same thread that placed the object to InInit or InUninit state (i.e. either from within the AutoInitSpan or AutoUninitSpan scope), it will succeed as well (but will not increase the number of callers).

Normally, calling addCaller() never blocks. However, if this method is called by a thread created from within the AutoInitSpan scope and this scope is still active (i.e. the object state is InInit), it will block until the AutoInitSpan destructor signals that it has finished initialization.

Also, addCaller() will block if the object is probing uninitialization on another thread with AutoMayUninitSpan (i.e. the object state is MayUninit). And again, the block will last until the AutoMayUninitSpan destructor signals that it has finished probing and the object is either ready again or will uninitialize shortly (so that addCaller() will fail).

When this method returns a failure, the caller must not use the object and should return the failed result code to its own caller.

Parameters:
aState Where to store the current object's state (can be used in overriden methods to determine the cause of the failure).
aLimited |true| to add a limited caller.
Returns:
S_OK on success or E_ACCESSDENIED on failure.
Note:
It is preferrable to use the addLimitedCaller() rather than calling this method with aLimited = |true|, for better self-descriptiveness.
See also:
addLimitedCaller()

releaseCaller()

Definition at line 152 of file VirtualBoxBase.cpp.

References Assert, util::AutoWriteLock::enter(), util::AutoWriteLock::leave(), LogFlowThisFunc, VirtualBoxBaseProto::mCallers, VirtualBoxBaseProto::mInitUninitSem, VirtualBoxBaseProto::mInitUninitWaiters, VirtualBoxBaseProto::mState, VirtualBoxBaseProto::mStateChangeThread, VirtualBoxBaseProto::mStateLock, VirtualBoxBaseProto::mZeroCallersSem, NIL_RTSEMEVENTMULTI, and RT_INDEFINITE_WAIT.

Referenced by VirtualBoxBaseProto::AutoMayUninitSpan::AutoMayUninitSpan().

{
    AutoWriteLock stateLock (mStateLock);

    HRESULT rc = E_ACCESSDENIED;

    if (mState == Ready || (aLimited && mState == Limited))
    {
        /* if Ready or allows Limited, increase the number of callers */
        ++ mCallers;
        rc = S_OK;
    }
    else
    if (mState == InInit || mState == MayUninit || mState == InUninit)
    {
        if (mStateChangeThread == RTThreadSelf())
        {
            /* Called from the same thread that is doing AutoInitSpan or
             * AutoUninitSpan or AutoMayUninitSpan, just succeed */
            rc = S_OK;
        }
        else if (mState == InInit || mState == MayUninit)
        {
            /* One of the two:
             *
             * 1) addCaller() is called by a "child" thread while the "parent"
             *    thread is still doing AutoInitSpan/AutoReinitSpan, so wait for
             *    the state to become either Ready/Limited or InitFailed (in
             *    case of init failure).
             *
             * 2) addCaller() is called while another thread is in
             *    AutoMayUninitSpan, so wait for the state to become either
             *    Ready or WillUninit.
             *
             * Note that in either case we increase the number of callers anyway
             * -- to prevent AutoUninitSpan from early completion if we are
             * still not scheduled to pick up the posted semaphore when uninit()
             * is called.
             */
            ++ mCallers;

            /* lazy semaphore creation */
            if (mInitUninitSem == NIL_RTSEMEVENTMULTI)
            {
                RTSemEventMultiCreate (&mInitUninitSem);
                Assert (mInitUninitWaiters == 0);
            }

            ++ mInitUninitWaiters;

            LogFlowThisFunc ((mState == InInit ?
                              "Waiting for AutoInitSpan/AutoReinitSpan to "
                              "finish...\n" :
                              "Waiting for AutoMayUninitSpan to finish...\n"));

            stateLock.leave();
            RTSemEventMultiWait (mInitUninitSem, RT_INDEFINITE_WAIT);
            stateLock.enter();

            if (-- mInitUninitWaiters == 0)
            {
                /* destroy the semaphore since no more necessary */
                RTSemEventMultiDestroy (mInitUninitSem);
                mInitUninitSem = NIL_RTSEMEVENTMULTI;
            }

            if (mState == Ready || (aLimited && mState == Limited))
                rc = S_OK;
            else
            {
                Assert (mCallers != 0);
                -- mCallers;
                if (mCallers == 0 && mState == InUninit)
                {
                    /* inform AutoUninitSpan ctor there are no more callers */
                    RTSemEventSignal (mZeroCallersSem);
                }
            }
        }
    }

    if (aState)
        *aState = mState;

    return rc;
}


Generated by  Doxygen 1.6.0   Back to index