VirtualBox

Changeset 52095 in vbox


Ignore:
Timestamp:
Jul 18, 2014 9:14:01 AM (11 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
95105
Message:

Main/Medium+AutoCaller+others: fix medium uninit deadlock caused by lock order violations, sometimes taking the caller before the media tree lock, sometimes after, plus a few other small fixes

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

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/idl/VirtualBox.xidl

    r52090 r52095  
    1365813658    </attribute>
    1365913659
    13660     <attribute name="parent" type="IMedium" readonly="yes">
     13660    <attribute name="parent" type="IMedium" readonly="yes" wrap-hint-server="passcaller">
    1366113661      <desc>
    1366213662        Parent of this medium (the medium this medium is directly based
     
    1366813668    </attribute>
    1366913669
    13670     <attribute name="children" type="IMedium" safearray="yes" readonly="yes">
     13670    <attribute name="children" type="IMedium" safearray="yes" readonly="yes" wrap-hint-server="passcaller">
    1367113671      <desc>
    1367213672        Children of this medium (all differencing media directly based
     
    1367613676    </attribute>
    1367713677
    13678     <attribute name="base" type="IMedium" readonly="yes">
     13678    <attribute name="base" type="IMedium" readonly="yes" wrap-hint-server="passcaller">
    1367913679      <desc>
    1368013680        Base medium of this medium.
  • trunk/src/VBox/Main/include/AutoCaller.h

    r51903 r52095  
    475475    bool uninitDone() { return mUninitDone; }
    476476
     477    void setSucceeded();
     478
    477479private:
    478480
  • trunk/src/VBox/Main/include/VirtualBoxImpl.h

    r50380 r52095  
    214214    int i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult);
    215215    void i_copyPathRelativeToConfig(const Utf8Str &strSource, Utf8Str &strTarget);
    216     HRESULT i_registerMedium(const ComObjPtr<Medium> &pMedium, ComObjPtr<Medium> *ppMedium, DeviceType_T argType);
     216    HRESULT i_registerMedium(const ComObjPtr<Medium> &pMedium, ComObjPtr<Medium> *ppMedium, DeviceType_T argType, AutoWriteLock &mediaTreeLock);
    217217    HRESULT i_unregisterMedium(Medium *pMedium);
    218218    void i_pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium);
  • trunk/src/VBox/Main/src-all/AutoCaller.cpp

    r52029 r52095  
    498498    mObj->getObjectState().autoUninitSpanDestructor();
    499499}
     500
     501/**
     502 * Marks the uninitializion as succeeded.
     503 *
     504 * Same as the destructor, and makes the destructor do nothing.
     505 */
     506void AutoUninitSpan::setSucceeded()
     507{
     508    /* do nothing if already uninitialized */
     509    if (mUninitDone)
     510        return;
     511
     512    mObj->getObjectState().autoUninitSpanDestructor();
     513    mUninitDone = true;
     514}
  • trunk/src/VBox/Main/src-server/MachineImplCloneVM.cpp

    r51498 r52095  
    12501250                            AutoWriteLock tlock(p->mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    12511251                            rc = p->mParent->i_registerMedium(pTarget, &pTarget,
    1252                                                             DeviceType_HardDisk);
     1252                                                            DeviceType_HardDisk,
     1253                                                            tlock);
    12531254                            if (FAILED(rc)) throw rc;
    12541255                        }
  • trunk/src/VBox/Main/src-server/MediumImpl.cpp

    r52078 r52095  
    934934    if (FAILED(rc)) return rc;
    935935
    936     if (!(m->formatObj->i_getCapabilities() & (   MediumFormatCapabilities_CreateFixed
    937                                               | MediumFormatCapabilities_CreateDynamic))
     936    if (!(m->formatObj->i_getCapabilities() & (  MediumFormatCapabilities_CreateFixed
     937                                               | MediumFormatCapabilities_CreateDynamic))
    938938       )
    939939    {
     
    970970        }
    971971
    972         rc = m->pVirtualBox->i_registerMedium(this, &pMedium, DeviceType_HardDisk);
     972        rc = m->pVirtualBox->i_registerMedium(this, &pMedium, DeviceType_HardDisk, treeLock);
    973973        Assert(this == pMedium || FAILED(rc));
    974974    }
     
    12661266     * get the actual state and the rest of the data, the user will have to call
    12671267     * COMGETTER(State). */
    1268 
    1269     AutoWriteLock treeLock(aVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    12701268
    12711269    /* load all children */
     
    12861284        if (FAILED(rc)) break;
    12871285
    1288         rc = m->pVirtualBox->i_registerMedium(pHD, &pHD, DeviceType_HardDisk);
     1286        AutoWriteLock treeLock(aVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     1287
     1288        rc = m->pVirtualBox->i_registerMedium(pHD, &pHD, DeviceType_HardDisk, treeLock);
    12891289        if (FAILED(rc)) break;
    12901290    }
     
    13681368void Medium::uninit()
    13691369{
     1370    /* It is possible that some previous/concurrent uninit has already cleared
     1371     * the pVirtualBox reference, and in this case we don't need to continue.
     1372     * Normally this would be handled through the AutoUninitSpan magic,
     1373     * however this cannot be done at this point as the media tree must be
     1374     * locked before reaching the AutoUninitSpan, otherwise deadlocks can
     1375     * happen due to*/
     1376    ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
     1377    if (!pVirtualBox)
     1378        return;
     1379
     1380    /* Caller must not hold the object or media tree lock over uninit(). */
     1381    Assert(!isWriteLockOnCurrentThread());
     1382    Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
     1383
     1384    AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     1385
    13701386    /* Enclose the state transition Ready->InUninit->NotReady */
    13711387    AutoUninitSpan autoUninitSpan(this);
     
    13741390
    13751391    if (!m->formatObj.isNull())
    1376     {
    1377         /* remove the caller reference we added in setFormat() */
    1378         m->formatObj->getObjectState().releaseCaller();
    13791392        m->formatObj.setNull();
    1380     }
    13811393
    13821394    if (m->state == MediumState_Deleting)
     
    13881400    else
    13891401    {
    1390         MediaList::iterator it;
    1391         for (it = m->llChildren.begin();
    1392             it != m->llChildren.end();
    1393             ++it)
    1394         {
    1395             Medium *pChild = *it;
     1402        MediaList llChildren(m->llChildren);
     1403        m->llChildren.clear();
     1404        autoUninitSpan.setSucceeded();
     1405
     1406        while (!llChildren.empty())
     1407        {
     1408            ComObjPtr<Medium> pChild = llChildren.front();
     1409            llChildren.pop_front();
    13961410            pChild->m->pParent.setNull();
     1411            treeLock.release();
    13971412            pChild->uninit();
    1398         }
    1399         m->llChildren.clear();          // this unsets all the ComPtrs and probably calls delete
     1413            treeLock.acquire();
     1414        }
    14001415
    14011416        if (m->pParent)
     
    17131728}
    17141729
    1715 HRESULT Medium::getParent(ComPtr<IMedium> &aParent)
    1716 {
     1730HRESULT Medium::getParent(AutoCaller &autoCaller, ComPtr<IMedium> &aParent)
     1731{
     1732    autoCaller.release();
     1733
     1734    /* It is possible that some previous/concurrent uninit has already cleared
     1735     * the pVirtualBox reference, see #uninit(). */
     1736    ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
     1737
    17171738    /* we access mParent */
    1718     AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     1739    AutoReadLock treeLock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL COMMA_LOCKVAL_SRC_POS);
     1740
     1741    autoCaller.add();
     1742    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    17191743
    17201744    m->pParent.queryInterfaceTo(aParent.asOutParam());
     
    17231747}
    17241748
    1725 HRESULT Medium::getChildren(std::vector<ComPtr<IMedium> > &aChildren)
    1726 {
     1749HRESULT Medium::getChildren(AutoCaller &autoCaller, std::vector<ComPtr<IMedium> > &aChildren)
     1750{
     1751    autoCaller.release();
     1752
     1753    /* It is possible that some previous/concurrent uninit has already cleared
     1754     * the pVirtualBox reference, see #uninit(). */
     1755    ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
     1756
    17271757    /* we access children */
    1728     AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     1758    AutoReadLock treeLock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL COMMA_LOCKVAL_SRC_POS);
     1759
     1760    autoCaller.add();
     1761    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    17291762
    17301763    MediaList children(this->i_getChildren());
     
    17361769}
    17371770
    1738 HRESULT Medium::getBase(ComPtr<IMedium> &aBase)
    1739 {
     1771HRESULT Medium::getBase(AutoCaller &autoCaller, ComPtr<IMedium> &aBase)
     1772{
     1773    autoCaller.release();
     1774
    17401775    /* i_getBase() will do callers/locking */
    17411776    i_getBase().queryInterfaceTo(aBase.asOutParam());
     
    32293264    }
    32303265
     3266    autoCaller.release();
     3267
    32313268    /* Save the error information now, the implicit restore when this goes
    32323269     * out of scope will throw away spurious additional errors created below. */
     
    35233560{
    35243561    ComObjPtr<Medium> pBase;
    3525     uint32_t level;
     3562
     3563    /* it is possible that some previous/concurrent uninit has already cleared
     3564     * the pVirtualBox reference, and in this case we don't need to continue */
     3565    ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
     3566    if (!pVirtualBox)
     3567        return pBase;
     3568
     3569    /* we access mParent */
     3570    AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    35263571
    35273572    AutoCaller autoCaller(this);
    35283573    AssertReturn(autoCaller.isOk(), pBase);
    35293574
    3530     /* we access mParent */
    3531     AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    3532 
    35333575    pBase = this;
    3534     level = 0;
     3576    uint32_t level = 0;
    35353577
    35363578    if (m->pParent)
     
    39714013HRESULT Medium::i_close(AutoCaller &autoCaller)
    39724014{
     4015    // must temporarily drop the caller, need the tree lock first
     4016    autoCaller.release();
     4017
    39734018    // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
    39744019    AutoMultiWriteLock2 multilock(&m->pVirtualBox->i_getMediaTreeLockHandle(),
    39754020                                  this->lockHandle()
    39764021                                  COMMA_LOCKVAL_SRC_POS);
     4022
     4023    autoCaller.add();
     4024    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    39774025
    39784026    LogFlowFunc(("ENTER for %s\n", i_getLocationFull().c_str()));
     
    40104058
    40114059        multilock.release();
     4060        // Release the AutoCaller now, as otherwise uninit() will simply hang.
     4061        // Needs to be done before mark the registries as modified and saving
     4062        // the registry, as otherwise there may be a deadlock with someone else
     4063        // closing this object while we're in i_saveModifiedRegistries(), which
     4064        // needs the media tree lock, which the other thread holds until after
     4065        // uninit() below.
     4066        autoCaller.release();
    40124067        i_markRegistriesModified();
    4013         // Release the AutoCalleri now, as otherwise uninit() will simply hang.
    4014         // Needs to be done before saving the registry, as otherwise there
    4015         // may be a deadlock with someone else closing this object while we're
    4016         // in i_saveModifiedRegistries(), which needs the media tree lock, which
    4017         // the other thread holds until after uninit() below.
    4018         // @todo redesign the locking here, as holding the locks over uninit
    4019         // causes lock order trouble which the lock validator can't detect
    4020         autoCaller.release();
    40214068        m->pVirtualBox->i_saveModifiedRegistries();
    4022         multilock.acquire();
    40234069    }
    40244070    else
    40254071    {
     4072        multilock.release();
    40264073        // release the AutoCaller, as otherwise uninit() will simply hang
    40274074        autoCaller.release();
     
    40794126        LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, i_getLocationFull().c_str() ));
    40804127
    4081         if (    !(m->formatObj->i_getCapabilities() & (   MediumFormatCapabilities_CreateDynamic
    4082                                                       | MediumFormatCapabilities_CreateFixed)))
     4128        if (    !(m->formatObj->i_getCapabilities() & (  MediumFormatCapabilities_CreateDynamic
     4129                                                       | MediumFormatCapabilities_CreateFixed)))
    40834130            throw setError(VBOX_E_NOT_SUPPORTED,
    40844131                           tr("Medium format '%s' does not support storage deletion"),
     
    61766223                            aFormat.c_str());
    61776224
    6178         /* reference the format permanently to prevent its unexpected
    6179          * uninitialization */
    6180         HRESULT rc = m->formatObj->getObjectState().addCaller(m->formatObj);
    6181         AssertComRCReturnRC(rc);
    6182 
    61836225        /* get properties (preinsert them as keys in the map). Note that the
    61846226         * map doesn't grow over the object life time since the set of
     
    66766718        AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    66776719        ComObjPtr<Medium> pMedium;
    6678         rc = m->pVirtualBox->i_registerMedium(this, &pMedium, DeviceType_HardDisk);
     6720        rc = m->pVirtualBox->i_registerMedium(this, &pMedium, DeviceType_HardDisk, treeLock);
    66796721        Assert(this == pMedium);
    66806722    }
     
    68636905         * better than breaking media registry consistency) */
    68646906        ComObjPtr<Medium> pMedium;
    6865         mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium, DeviceType_HardDisk);
     6907        mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium, DeviceType_HardDisk, treeLock);
    68666908        Assert(pTarget == pMedium);
    68676909
     
    70987140            ComObjPtr<Medium> pMedium;
    70997141            rc2 = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
    7100                                                  DeviceType_HardDisk);
     7142                                                   DeviceType_HardDisk,
     7143                                                   treeLock);
    71017144            AssertComRC(rc2);
    71027145        }
     
    71827225
    71837226            if (task.isAsync() || pMedium != this)
     7227            {
     7228                treeLock.release();
    71847229                pMedium->uninit();
     7230                treeLock.acquire();
     7231            }
    71857232        }
    71867233    }
     
    74267473    {
    74277474        /* we set mParent & children() */
    7428         AutoWriteLock alock2(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     7475        AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    74297476
    74307477        Assert(pTarget->m->pParent.isNull());
     
    74437490            ComObjPtr<Medium> pMedium;
    74447491            mrc = pParent->m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
    7445                                                           DeviceType_HardDisk);
     7492                                                            DeviceType_HardDisk,
     7493                                                            treeLock);
    74467494            Assert(   FAILED(mrc)
    74477495                   || pTarget == pMedium);
     
    74587506            ComObjPtr<Medium> pMedium;
    74597507            mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
    7460                                                  DeviceType_HardDisk);
     7508                                                   DeviceType_HardDisk,
     7509                                                   treeLock);
    74617510            Assert(   FAILED(mrc)
    74627511                   || pTarget == pMedium);
     
    82148263    {
    82158264        /* we set mParent & children() */
    8216         AutoWriteLock alock2(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
     8265        AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
    82178266
    82188267        Assert(m->pParent.isNull());
     
    82318280            ComObjPtr<Medium> pMedium;
    82328281            mrc = pParent->m->pVirtualBox->i_registerMedium(this, &pMedium,
    8233                                                           DeviceType_HardDisk);
     8282                                                            DeviceType_HardDisk,
     8283                                                            treeLock);
    82348284            Assert(this == pMedium);
    82358285            eik.fetch();
    82368286
    82378287            if (FAILED(mrc))
     8288            {
     8289                treeLock.acquire();
    82388290                /* break parent association on failure to register */
    82398291                this->i_deparent();     // removes target from parent
     8292            }
    82408293        }
    82418294        else
     
    82448297            eik.restore();
    82458298            ComObjPtr<Medium> pMedium;
    8246             mrc = m->pVirtualBox->i_registerMedium(this, &pMedium, DeviceType_HardDisk);
     8299            mrc = m->pVirtualBox->i_registerMedium(this, &pMedium, DeviceType_HardDisk, treeLock);
    82478300            Assert(this == pMedium);
    82488301            eik.fetch();
  • trunk/src/VBox/Main/src-server/SnapshotImpl.cpp

    r51903 r52095  
    34693469
    34703470        // then, register again
    3471         rc = mParent->i_registerMedium(pDeleteRec->mpTarget, &pDeleteRec->mpTarget, DeviceType_HardDisk);
     3471        rc = mParent->i_registerMedium(pDeleteRec->mpTarget, &pDeleteRec->mpTarget, DeviceType_HardDisk, treeLock);
    34723472        AssertComRC(rc);
     3473        treeLock.acquire();
    34733474    }
    34743475    else
  • trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp

    r51903 r52095  
    675675        if (FAILED(rc)) return rc;
    676676
    677         rc = i_registerMedium(pHardDisk, &pHardDisk, DeviceType_HardDisk);
     677        rc = i_registerMedium(pHardDisk, &pHardDisk, DeviceType_HardDisk, treeLock);
    678678        if (FAILED(rc)) return rc;
    679679    }
     
    695695        if (FAILED(rc)) return rc;
    696696
    697         rc = i_registerMedium(pImage, &pImage, DeviceType_DVD);
     697        rc = i_registerMedium(pImage, &pImage, DeviceType_DVD, treeLock);
    698698        if (FAILED(rc)) return rc;
    699699    }
     
    715715        if (FAILED(rc)) return rc;
    716716
    717         rc = i_registerMedium(pImage, &pImage, DeviceType_Floppy);
     717        rc = i_registerMedium(pImage, &pImage, DeviceType_Floppy, treeLock);
    718718        if (FAILED(rc)) return rc;
    719719    }
     
    18311831        if (SUCCEEDED(rc))
    18321832        {
    1833             rc = i_registerMedium(pMedium, &pMedium, aDeviceType);
     1833            rc = i_registerMedium(pMedium, &pMedium, aDeviceType, treeLock);
    18341834
    18351835            treeLock.release();
     
    29482948
    29492949/**
    2950  *  @note Locks this object for reading.
     2950 *  @note Locks the list of other objects for reading.
    29512951 */
    29522952ComObjPtr<GuestOSType> VirtualBox::i_getUnknownOSType()
    29532953{
    29542954    ComObjPtr<GuestOSType> type;
    2955     AutoCaller autoCaller(this);
    2956     AssertComRCReturn(autoCaller.rc(), type);
    29572955
    29582956    /* unknown type must always be the first */
     
    41554153 *                  created.
    41564154 * @param argType   Either DeviceType_HardDisk, DeviceType_DVD or DeviceType_Floppy.
     4155 * @param mediaTreeLock Reference to the AutoWriteLock holding the media tree
     4156 *                  lock, necessary to release it in the right spot.
    41574157 * @return
    41584158 */
    41594159HRESULT VirtualBox::i_registerMedium(const ComObjPtr<Medium> &pMedium,
    41604160                                     ComObjPtr<Medium> *ppMedium,
    4161                                      DeviceType_T argType)
     4161                                     DeviceType_T argType,
     4162                                     AutoWriteLock &mediaTreeLock)
    41624163{
    41634164    AssertReturn(pMedium != NULL, E_INVALIDARG);
    41644165    AssertReturn(ppMedium != NULL, E_INVALIDARG);
     4166
     4167    // caller must hold the media tree write lock
     4168    Assert(i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
    41654169
    41664170    AutoCaller autoCaller(this);
     
    41904194    }
    41914195
    4192     // caller must hold the media tree write lock
    4193     Assert(i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
    4194 
    41954196    Guid id;
    41964197    Utf8Str strLocationFull;
     
    42324233            m->mapHardDisks[id] = pMedium;
    42334234
     4235        mediumCaller.release();
     4236        mediaTreeLock.release();
    42344237        *ppMedium = pMedium;
    42354238    }
     
    42414244        // have a caller pending.
    42424245        mediumCaller.release();
     4246        // release media tree lock, must not be held at uninit time.
     4247        mediaTreeLock.release();
     4248        // must not hold the media tree write lock any more
     4249        Assert(!i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
    42434250        *ppMedium = pDupMedium;
    42444251    }
     
    48004807    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
    48014808
     4809    // need it below, in findDHCPServerByNetworkName (reading) and in
     4810    // m->allDHCPServers.addChild, so need to get it here to avoid lock
     4811    // order trouble with dhcpServerCaller
     4812    AutoWriteLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
     4813
    48024814    AutoCaller dhcpServerCaller(aDHCPServer);
    48034815    AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
Note: See TracChangeset for help on using the changeset viewer.

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