VirtualBox

Changeset 59996 in vbox for trunk/src


Ignore:
Timestamp:
Mar 11, 2016 3:27:55 PM (9 years ago)
Author:
vboxsync
Message:

Main/VirtualBox: postpone the error reporting from VirtualBox object creation to the method calls of the object. COM loses the error (replaces it by REGDB_E_CLASSNOTREG), making troubleshooting very difficult. XPCOM wouldn't need this, but it is applied everywhere for maximum consistency. Many changes elsewhere to propagate the information correctly, and also fixes many outdated comments.

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

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/include/AutoCaller.h

    r55401 r59996  
    66
    77/*
    8  * Copyright (C) 2006-2014 Oracle Corporation
     8 * Copyright (C) 2006-2016 Oracle Corporation
    99 *
    1010 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    334334     * to the NotReady state using AutoUninitSpan.
    335335     */
    336     void setFailed() { mResult = Failed; }
     336    void setFailed(HRESULT rc = E_ACCESSDENIED)
     337    {
     338        mResult = Failed;
     339        mFailedRC = rc;
     340        mpFailedEI = new ErrorInfo();
     341    }
    337342
    338343    /** Returns the current initialization result. */
     
    347352    Result mResult : 3; // must be at least total number of bits + 1 (sign)
    348353    bool mOk : 1;
     354    HRESULT mFailedRC;
     355    ErrorInfo *mpFailedEI;
    349356};
    350357
  • trunk/src/VBox/Main/include/ObjectState.h

    r55401 r59996  
    66
    77/*
    8  * Copyright (C) 2006-2014 Oracle Corporation
     8 * Copyright (C) 2006-2016 Oracle Corporation
    99 *
    1010 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    2222#include "VBox/com/defs.h"
    2323#include "VBox/com/AutoLock.h"
     24#include "VBox/com/ErrorInfo.h"
    2425
    2526// Forward declaration needed, but nothing more.
     
    9798
    9899    bool autoInitSpanConstructor(State aExpectedState);
    99     void autoInitSpanDestructor(State aNewState);
     100    void autoInitSpanDestructor(State aNewState, HRESULT aFailedRC, com::ErrorInfo *aFailedEI);
    100101    State autoUninitSpanConstructor();
    101102    void autoUninitSpanDestructor();
     
    114115    /** Thread that caused the last state change */
    115116    RTTHREAD mStateChangeThread;
     117    /** Result code for failed object initialization */
     118    HRESULT mFailedRC;
     119    /** Error information for failed object initialization */
     120    com::ErrorInfo *mpFailedEI;
    116121    /** Total number of active calls to this object */
    117122    unsigned mCallers;
  • trunk/src/VBox/Main/src-all/AutoCaller.cpp

    r52095 r59996  
    77
    88/*
    9  * Copyright (C) 2006-2014 Oracle Corporation
     9 * Copyright (C) 2006-2016 Oracle Corporation
    1010 *
    1111 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    4444    mStateChangeThread = NIL_RTTHREAD;
    4545    mCallers = 0;
     46    mFailedRC = S_OK;
     47    mpFailedEI = NULL;
    4648    mZeroCallersSem = NIL_RTSEMEVENT;
    4749    mInitUninitSem = NIL_RTSEMEVENTMULTI;
     
    5860    mStateChangeThread = NIL_RTTHREAD;
    5961    mState = NotReady;
     62    mFailedRC = S_OK;
     63    if (mpFailedEI)
     64    {
     65        delete mpFailedEI;
     66        mpFailedEI = NULL;
     67    }
    6068    mObj = NULL;
    6169}
     
    195203        if (mState == Limited)
    196204            rc = mObj->setError(rc, "The object functionality is limited");
     205        else if (FAILED(mFailedRC) && mFailedRC != E_ACCESSDENIED)
     206        {
     207            /* replay recorded error information */
     208            if (mpFailedEI)
     209                ErrorInfoKeeper eik(*mpFailedEI);
     210            rc = mFailedRC;
     211        }
    197212        else
    198213            rc = mObj->setError(rc, "The object is not ready");
     
    251266    AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
    252267
     268    mFailedRC = S_OK;
     269    if (mpFailedEI)
     270    {
     271        delete mpFailedEI;
     272        mpFailedEI = NULL;
     273    }
     274
    253275    if (mState == aExpectedState)
    254276    {
     
    260282}
    261283
    262 void ObjectState::autoInitSpanDestructor(State aNewState)
     284void ObjectState::autoInitSpanDestructor(State aNewState, HRESULT aFailedRC, com::ErrorInfo *apFailedEI)
    263285{
    264286    AutoWriteLock stateLock(mStateLock COMMA_LOCKVAL_SRC_POS);
     
    271293         * during InInit), signal that InInit is finished and they may go on. */
    272294        RTSemEventMultiSignal(mInitUninitSem);
     295    }
     296
     297    if (aNewState == InitFailed)
     298    {
     299        mFailedRC = aFailedRC;
     300        /* apFailedEI may be NULL, when there is no explicit setFailed() call,
     301         * which also implies that aFailedRC is S_OK. This case is used by
     302         * objects (the majority) which don't want delayed error signalling. */
     303        mpFailedEI = apFailedEI;
     304    }
     305    else
     306    {
     307        Assert(SUCCEEDED(aFailedRC));
     308        Assert(apFailedEI == NULL);
     309        Assert(mpFailedEI == NULL);
    273310    }
    274311
     
    376413    : mObj(aObj),
    377414      mResult(aResult),
    378       mOk(false)
     415      mOk(false),
     416      mFailedRC(S_OK),
     417      mpFailedEI(NULL)
    379418{
    380419    Assert(mObj);
     
    394433    /* if the state was other than NotReady, do nothing */
    395434    if (!mOk)
     435    {
     436        Assert(SUCCEEDED(mFailedRC));
     437        Assert(mpFailedEI == NULL);
    396438        return;
     439    }
    397440
    398441    ObjectState::State newState;
     
    403446    else
    404447        newState = ObjectState::InitFailed;
    405     mObj->getObjectState().autoInitSpanDestructor(newState);
     448    mObj->getObjectState().autoInitSpanDestructor(newState, mFailedRC, mpFailedEI);
     449    mFailedRC = S_OK;
     450    mpFailedEI = NULL; /* now owned by ObjectState instance */
    406451    if (newState == ObjectState::InitFailed)
    407452    {
    408453        /* call uninit() to let the object uninit itself after failed init() */
    409454        mObj->uninit();
    410         /* Note: the object may no longer exist here (for example, it can call
    411          * the destructor in uninit()) */
    412455    }
    413456}
     
    453496    else
    454497        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. */
     498    mObj->getObjectState().autoInitSpanDestructor(newState, S_OK, NULL);
     499    /* If later AutoReinitSpan can truly fail (today there is no way) then
     500     * in this place there needs to be an mObj->uninit() call just like in
     501     * the AutoInitSpan destructor. In that case it might make sense to
     502     * let AutoReinitSpan inherit from AutoInitSpan, as the code can be
     503     * made (almost) identical. */
    458504}
    459505
  • trunk/src/VBox/Main/src-client/ConsoleImpl.cpp

    r59252 r59996  
    140140 * 1. The user must check for #rc() before using the created structure
    141141 *    (e.g. passing it as a thread function argument). If #rc() returns a
    142  *    failure, the Console object may not be used by the task (see
    143  *    Console::addCaller() for more details).
     142 *    failure, the Console object may not be used by the task.
    144143 * 2. On successful initialization, the structure keeps the Console caller
    145144 *    until destruction (to ensure Console remains in the Ready state and won't
     
    94629461    ComObjPtr<Console> pConsole = task->mConsole;
    94639462
    9464     /* Note: no need to use addCaller() because VMPowerUpTask does that */
     9463    /* Note: no need to use AutoCaller because VMPowerUpTask does that */
    94659464
    94669465    /* The lock is also used as a signal from the task initiator (which
     
    99879986        const ComObjPtr<Console> &that = task->mConsole;
    99889987
    9989         /* Note: no need to use addCaller() to protect Console because VMTask does
     9988        /* Note: no need to use AutoCaller to protect Console because VMTask does
    99909989         * that */
    99919990
  • trunk/src/VBox/Main/src-client/SessionImpl.cpp

    r57326 r59996  
    11591159         *
    11601160         *  bird: Seems E_ACCESSDENIED is what gets returned these days; see
    1161          *        VirtualBoxBase::addCaller.
     1161         *        ObjectState::addCaller.
    11621162         */
    11631163        if (mType != SessionType_WriteLock && (rc == E_UNEXPECTED || rc == E_ACCESSDENIED))
  • trunk/src/VBox/Main/src-client/VirtualBoxClientImpl.cpp

    r52442 r59996  
    55
    66/*
    7  * Copyright (C) 2010-2014 Oracle Corporation
     7 * Copyright (C) 2010-2016 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    7878
    7979    rc = mData.m_pVirtualBox.createLocalObject(CLSID_VirtualBox);
    80     AssertComRCReturnRC(rc);
     80    if (FAILED(rc))
     81        return rc;
     82
     83    /* Error return is postponed to method calls, fetch info now. */
     84    ULONG rev;
     85    rc = mData.m_pVirtualBox->COMGETTER(Revision)(&rev);
     86    if (FAILED(rc))
     87        return rc;
    8188
    8289    rc = unconst(mData.m_pEventSource).createObject();
  • trunk/src/VBox/Main/src-server/MachineImpl.cpp

    r59926 r59996  
    831831         * sessions. Since in this case we are definitely called by
    832832         * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
    833          * won't happen on the client watcher thread (because it does
    834          * VirtualBox::addCaller() for the duration of the
    835          * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
     833         * won't happen on the client watcher thread (because it has a
     834         * VirtualBox caller for the duration of the
     835         * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
    836836         * cannot happen until the VirtualBox caller is released). This is
    837837         * important, because SessionMachine::uninit() cannot correctly operate
     
    1247412474/**
    1247512475 *  Uninitializes this session object. If the reason is other than
    12476  *  Uninit::Unexpected, then this method MUST be called from #checkForDeath()
     12476 *  Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
    1247712477 *  or the client watcher code.
    1247812478 *
     
    1259612596    if (aReason == Uninit::Unexpected)
    1259712597    {
    12598         /* Uninitialization didn't come from #checkForDeath(), so tell the
     12598        /* Uninitialization didn't come from #i_checkForDeath(), so tell the
    1259912599         * client watcher thread to update the set of machines that have open
    1260012600         * sessions. */
     
    1265412654
    1265512655    /*
    12656      *  An expected uninitialization can come only from #checkForDeath().
     12656     *  An expected uninitialization can come only from #i_checkForDeath().
    1265712657     *  Otherwise it means that something's gone really wrong (for example,
    1265812658     *  the Session implementation has released the VirtualBox reference
     
    1333313333
    1333413334        /* go to the closing state (essential for all open*Session() calls and
    13335          * for #checkForDeath()) */
     13335         * for #i_checkForDeath()) */
    1333613336        Assert(mData->mSession.mState == SessionState_Locked);
    1333713337        mData->mSession.mState = SessionState_Unlocking;
     
    1335213352        }
    1335313353
    13354         /*  Create the progress object the client will use to wait until
    13355          * #checkForDeath() is called to uninitialize this session object after
     13354        /* Create the progress object the client will use to wait until
     13355         * #i_checkForDeath() is called to uninitialize this session object after
    1335613356         * it releases the IPC semaphore.
    1335713357         * Note! Because we're "reusing" mProgress here, this must be a proxy
     
    1472014720         * process in this case is waiting inside Session::close() for the
    1472114721         * "end session" process object to complete, while #uninit() called by
    14722          * #checkForDeath() on the Watcher thread is waiting for the pending
     14722         * #i_checkForDeath() on the Watcher thread is waiting for the pending
    1472314723         * operation to complete. For now, we accept this inconsistent behavior
    1472414724         * and simply do nothing here. */
  • trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp

    r59571 r59996  
    55
    66/*
    7  * Copyright (C) 2006-2015 Oracle Corporation
     7 * Copyright (C) 2006-2016 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    373373    LogFlowThisFunc(("Version: %s, Package: %s, API Version: %s\n", sVersion.c_str(), sPackageType.c_str(), sAPIVersion.c_str()));
    374374
    375     /* Get the VirtualBox home directory. */
    376     {
    377         char szHomeDir[RTPATH_MAX];
    378         int vrc = com::GetVBoxUserHomeDirectory(szHomeDir, sizeof(szHomeDir));
    379         if (RT_FAILURE(vrc))
    380             return setError(E_FAIL,
    381                             tr("Could not create the VirtualBox home directory '%s' (%Rrc)"),
    382                             szHomeDir, vrc);
    383 
    384         unconst(m->strHomeDir) = szHomeDir;
    385     }
    386 
    387     LogRel(("Home directory: '%s'\n", m->strHomeDir.c_str()));
    388 
    389     i_reportDriverVersions();
    390 
    391     /* compose the VirtualBox.xml file name */
    392     unconst(m->strSettingsFilePath) = Utf8StrFmt("%s%c%s",
    393                                                  m->strHomeDir.c_str(),
    394                                                  RTPATH_DELIMITER,
    395                                                  VBOX_GLOBAL_SETTINGS_FILE);
     375    /* Important: DO NOT USE any kind of "early return" (except the single
     376     * one above, checking the init span success) in this method. It is vital
     377     * for correct error handling that it has only one point of return, which
     378     * does all the magic on COM to signal object creation success and
     379     * reporting the error later for every API method. COM translates any
     380     * unsuccessful object creation to REGDB_E_CLASSNOTREG errors or similar
     381     * unhelpful ones which cause us a lot of grief with troubleshooting. */
     382
    396383    HRESULT rc = S_OK;
    397384    bool fCreate = false;
    398385    try
    399386    {
     387        /* Get the VirtualBox home directory. */
     388        {
     389            char szHomeDir[RTPATH_MAX];
     390            int vrc = com::GetVBoxUserHomeDirectory(szHomeDir, sizeof(szHomeDir));
     391            if (RT_FAILURE(vrc))
     392                throw setError(E_FAIL,
     393                               tr("Could not create the VirtualBox home directory '%s' (%Rrc)"),
     394                               szHomeDir, vrc);
     395
     396            unconst(m->strHomeDir) = szHomeDir;
     397        }
     398
     399        LogRel(("Home directory: '%s'\n", m->strHomeDir.c_str()));
     400
     401        i_reportDriverVersions();
     402
     403        /* compose the VirtualBox.xml file name */
     404        unconst(m->strSettingsFilePath) = Utf8StrFmt("%s%c%s",
     405                                                     m->strHomeDir.c_str(),
     406                                                     RTPATH_DELIMITER,
     407                                                     VBOX_GLOBAL_SETTINGS_FILE);
    400408        // load and parse VirtualBox.xml; this will throw on XML or logic errors
    401409        try
     
    511519
    512520            ComObjPtr<NATNetwork> pNATNetwork;
    513             if (SUCCEEDED(rc = pNATNetwork.createObject()))
    514             {
    515                 rc = pNATNetwork->init(this, net);
    516                 AssertComRCReturnRC(rc);
    517             }
    518 
     521            rc = pNATNetwork.createObject();
     522            AssertComRCThrowRC(rc);
     523            rc = pNATNetwork->init(this, net);
     524            AssertComRCThrowRC(rc);
    519525            rc = i_registerNATNetwork(pNATNetwork, false /* aSaveRegistry */);
    520             AssertComRCReturnRC(rc);
     526            AssertComRCThrowRC(rc);
    521527        }
    522528
     
    579585    }
    580586
    581     /* Confirm a successful initialization when it's the case */
    582     if (SUCCEEDED(rc))
    583         autoInitSpan.setSucceeded();
    584 
    585587#ifdef VBOX_WITH_EXTPACK
    586588    /* Let the extension packs have a go at things. */
     
    592594#endif
    593595
    594     LogFlowThisFunc(("rc=%08X\n", rc));
     596    /* Confirm a successful initialization when it's the case. Must be last,
     597     * as on failure it will uninitialize the object. */
     598    if (SUCCEEDED(rc))
     599        autoInitSpan.setSucceeded();
     600    else
     601        autoInitSpan.setFailed(rc);
     602
     603    LogFlowThisFunc(("rc=%hrc\n", rc));
    595604    LogFlowThisFuncLeave();
    596605    LogFlow(("===========================================================\n"));
    597     return rc;
     606    /* Unconditionally return success, because the error return is delayed to
     607     * the attribute/method calls through the Zombie object state. */
     608    return S_OK;
    598609}
    599610
     
    735746void VirtualBox::uninit()
    736747{
    737     Assert(!m->uRegistryNeedsSaving);
    738     if (m->uRegistryNeedsSaving)
    739         i_saveSettings();
     748    /* Must be done outside the AutoUninitSpan, as it expects AutoCaller to
     749     * be successful. This needs additional checks to protect against double
     750     * uninit, as then the pointer is NULL. */
     751    if (RT_VALID_PTR(m))
     752    {
     753        Assert(!m->uRegistryNeedsSaving);
     754        if (m->uRegistryNeedsSaving)
     755            i_saveSettings();
     756    }
    740757
    741758    /* Enclose the state transition Ready->InUninit->NotReady */
     
    840857    // clean up our instance data
    841858    delete m;
     859    m = NULL;
    842860
    843861    /* Unload hard disk plugin backends. */
     
    12591277                               firmwareDesc[i].fileName);
    12601278        int rc = i_calculateFullPath(shortName, fullName);
    1261         AssertRCReturn(rc, rc);
     1279        AssertRCReturn(rc, VBOX_E_IPRT_ERROR);
    12621280        if (RTFileExists(fullName.c_str()))
    12631281        {
     
    12691287        char pszVBoxPath[RTPATH_MAX];
    12701288        rc = RTPathExecDir(pszVBoxPath, RTPATH_MAX);
    1271         AssertRCReturn(rc, rc);
     1289        AssertRCReturn(rc, VBOX_E_IPRT_ERROR);
    12721290        fullName = Utf8StrFmt("%s%c%s",
    12731291                              pszVBoxPath,
     
    28122830
    28132831    AutoCaller autoCaller(this);
    2814     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
     2832    AssertComRCReturn(autoCaller.rc(), FALSE);
    28152833
    28162834    BOOL allowChange = TRUE;
     
    31803198
    31813199    AutoCaller autoCaller(this);
    3182     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
     3200    AssertComRCReturnRC(autoCaller.rc());
    31833201
    31843202    {
     
    41414159{
    41424160    AutoCaller autoCaller(this);
    4143     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
     4161    AssertComRCReturnRC(autoCaller.rc());
    41444162
    41454163    AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
    41464164    AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
     4165
     4166    i_unmarkRegistryModified(i_getGlobalRegistryId());
    41474167
    41484168    HRESULT rc = S_OK;
     
    43154335
    43164336    AutoCaller autoCaller(this);
    4317     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
     4337    AssertComRCReturnRC(autoCaller.rc());
    43184338
    43194339    AutoCaller mediumCaller(pMedium);
    4320     AssertComRCReturn(mediumCaller.rc(), mediumCaller.rc());
     4340    AssertComRCReturnRC(mediumCaller.rc());
    43214341
    43224342    const char *pszDevType = NULL;
     
    44214441
    44224442    AutoCaller autoCaller(this);
    4423     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
     4443    AssertComRCReturnRC(autoCaller.rc());
    44244444
    44254445    AutoCaller mediumCaller(pMedium);
    4426     AssertComRCReturn(mediumCaller.rc(), mediumCaller.rc());
     4446    AssertComRCReturnRC(mediumCaller.rc());
    44274447
    44284448    // caller must hold the media tree write lock
     
    45114531
    45124532    AutoCaller autoCaller(this);
    4513     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
     4533    AssertComRCReturnRC(autoCaller.rc());
    45144534
    45154535    MediaList llMedia2Close;
     
    50075027
    50085028    AutoCaller autoCaller(this);
    5009     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
     5029    AssertComRCReturnRC(autoCaller.rc());
    50105030
    50115031    // Acquire a lock on the VirtualBox object early to avoid lock order issues
     
    50185038
    50195039    AutoCaller dhcpServerCaller(aDHCPServer);
    5020     AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
     5040    AssertComRCReturnRC(dhcpServerCaller.rc());
    50215041
    50225042    Bstr name;
     
    50695089
    50705090    AutoCaller autoCaller(this);
    5071     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
     5091    AssertComRCReturnRC(autoCaller.rc());
    50725092
    50735093    AutoCaller dhcpServerCaller(aDHCPServer);
    5074     AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
     5094    AssertComRCReturnRC(dhcpServerCaller.rc());
    50755095
    50765096    AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
     
    52515271
    52525272    AutoCaller autoCaller(this);
    5253     AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
     5273    AssertComRCReturnRC(autoCaller.rc());
    52545274
    52555275    AutoCaller natNetworkCaller(aNATNetwork);
    5256     AssertComRCReturn(natNetworkCaller.rc(), natNetworkCaller.rc());
     5276    AssertComRCReturnRC(natNetworkCaller.rc());
    52575277
    52585278    Bstr name;
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