VirtualBox

Ignore:
Timestamp:
Nov 12, 2008 8:06:37 PM (16 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
39231
Message:

Main: Improved hard disk tree lock usage to avoid possible deadlocks when walking the hard disk tree upwards (from children to parents).

File:
1 edited

Legend:

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

    r14123 r14143  
    176176/**
    177177 * Helper class for merge operations.
     178 *
     179 * @note It is assumed that when modifying methods of this class are called,
     180 *       HardDisk2::treeLock() is held in read mode.
    178181 */
    179182class HardDisk2::MergeChain : public HardDisk2::List,
     
    571574 * @param aParent       Parent hard disk or NULL for a root hard disk.
    572575 * @param aNode         <HardDisk> settings node.
     576 *
     577 * @note Locks VirtualBox lock for writing, treeLock() for writing.
    573578 */
    574579HRESULT HardDisk2::init (VirtualBox *aVirtualBox, HardDisk2 *aParent,
     
    587592    /* share VirtualBox and parent weakly */
    588593    unconst (mVirtualBox) = aVirtualBox;
    589     mParent = aParent;
    590594
    591595    /* register with VirtualBox/parent early, since uninit() will
    592596     * unconditionally unregister on failure */
    593     if (!aParent)
     597    if (aParent == NULL)
    594598        aVirtualBox->addDependentChild (this);
    595     else
     599
     600    {
     601        /* we set mParent */
     602        AutoWriteLock treeLock (this->treeLock());
     603
     604        mParent = aParent;
    596605        aParent->addDependentChild (this);
     606    }
    597607
    598608    /* see below why we don't call queryInfo() (and therefore treat the medium
     
    670680 * @note All children of this hard disk get uninitialized by calling their
    671681 *       uninit() methods.
     682 *
     683 * @note Locks treeLock() for writing, VirtualBox for writing.
    672684 */
    673685void HardDisk2::uninit()
     
    683695         * Reparenting has already been done so don't touch it here (we are
    684696         * now orphans and remoeDependentChild() will assert) */
     697
     698        Assert (mParent.isNull());
    685699    }
    686700    else
    687701    {
     702        /* we uninit children and reset mParent
     703         * and VirtualBox::removeDependentChild() needs a write lock */
     704        AutoMultiWriteLock2 alock (mVirtualBox->lockHandle(), this->treeLock());
     705
    688706        uninitDependentChildren();
    689707
    690         if (mParent)
     708        if (!mParent.isNull())
     709        {
    691710            mParent->removeDependentChild (this);
     711            mParent.setNull();
     712        }
    692713        else
    693714            mVirtualBox->removeDependentChild (this);
    694715    }
    695716
    696     mParent.setNull();
    697717    unconst (mVirtualBox).setNull();
    698718}
     
    752772        return S_OK;
    753773    }
     774
     775    /* we access mParent & children() */
     776    AutoReadLock treeLock (this->treeLock());
    754777
    755778    /* cannot change the type of a differencing hard disk */
     
    803826    CheckComRCReturnRC (autoCaller.rc());
    804827
    805     AutoReadLock alock (this);
     828    /* we access mParent */
     829    AutoReadLock treeLock (this->treeLock());
    806830
    807831    mParent.queryInterfaceTo (aParent);
     
    818842    CheckComRCReturnRC (autoCaller.rc());
    819843
    820     AutoReadLock alock (this);
     844    /* we access children */
     845    AutoReadLock treeLock (this->treeLock());
    821846
    822847    SafeIfaceArray <IHardDisk2> children (this->children());
     
    863888
    864889        AutoReadLock alock (this);
     890
     891        /* we access mParent */
     892        AutoReadLock treeLock (this->treeLock());
    865893
    866894        if (mParent.isNull())
     
    10811109 * @param aNewPath  New path (full).
    10821110 *
    1083  * @note Locks this object and all children for writing.
     1111 * @note Locks treeLock() for reading, this object and all children for writing.
    10841112 */
    10851113void HardDisk2::updatePaths (const char *aOldPath, const char *aNewPath)
     
    10921120
    10931121    AutoWriteLock alock (this);
     1122
     1123    /* we access children() */
     1124    AutoReadLock treeLock (this->treeLock());
    10941125
    10951126    updatePath (aOldPath, aNewPath);
     
    11141145 *                  (zero for the root), may be @c NULL.
    11151146 *
    1116  * @note This method may return an uninitialized object if it happens in
    1117  *       parallel with such an operation as the VirtualBox shutdown or hard disk
    1118  *       merge.
    1119  *
    1120  * @note Locks this object and ancestors for reading. Neither this object nor
    1121  *       its ancestors may be locked or have active callers on the current
    1122  *       thread otherwise a deadlock or an endless loop will occur.
     1147 * @note Locks treeLock() for reading.
    11231148 */
    11241149ComObjPtr <HardDisk2> HardDisk2::root (uint32_t *aLevel /*= NULL*/)
    11251150{
    1126     /* Note: This method locks hard disks sequentially to avoid breaking the
    1127      * {parent,child} lock order which is impossible to follow here. As a
    1128      * result, any hard disk in the chain may become uninitialized before this
    1129      * method finishes walking (for example, as a result of the merge operation
    1130      * which removes hard disks from the chain). For this reason, we retry the
    1131      * walk from the beginnig each time it happens (because we must always
    1132      * return a non-null object).
    1133      *
    1134      * As the worst case, we end up having ourselves uninitialized (either
    1135      * because VirtualBox has initiated the shutdown procedure, or because we
    1136      * were closed/deleted/etc). In this case, we just return ourselves to avoid
    1137      * the endless loop.
    1138      */
    1139 
    11401151    ComObjPtr <HardDisk2> root;
    11411152    uint32_t level;
    11421153
    1143     do
    1144     {
    1145         root = this;
    1146         level = 0;
    1147 
    1148         do
    1149         {
    1150             AutoCaller autoCaller (root);
    1151             if (!autoCaller.isOk())
    1152             {
    1153                 /* break the endless loop, see above */
    1154                 if (root == this)
    1155                     break;
    1156 
    1157                 /* cause to start over with this object */
    1158                 root.setNull();
    1159                 break;
    1160             }
    1161 
    1162             AutoReadLock alock (root);
     1154    AutoCaller autoCaller (this);
     1155    AssertReturn (autoCaller.isOk(), root);
     1156
     1157    /* we access mParent */
     1158    AutoReadLock treeLock (this->treeLock());
     1159
     1160    root = this;
     1161    level = 0;
     1162
     1163    if (!mParent.isNull())
     1164    {
     1165        for (;;)
     1166        {
     1167            AutoCaller rootCaller (root);
     1168            AssertReturn (rootCaller.isOk(), root);
    11631169
    11641170            if (root->mParent.isNull())
     
    11681174            ++ level;
    11691175        }
    1170         while (true);
    1171     }
    1172     while (root.isNull());
     1176    }
    11731177
    11741178    if (aLevel != NULL)
     
    11831187 * type and posterity, not to the current media state.
    11841188 *
    1185  * @note Locks this object for reading.
     1189 * @note Locks this object and treeLock() for reading.
    11861190 */
    11871191bool HardDisk2::isReadOnly()
     
    11911195
    11921196    AutoReadLock alock (this);
     1197
     1198    /* we access children */
     1199    AutoReadLock treeLock (this->treeLock());
    11931200
    11941201    switch (mm.type)
     
    12271234 * @param aaParentNode  Parent <HardDisks> or <HardDisk> node.
    12281235 *
    1229  * @note Locks this object for reading.
     1236 * @note Locks this object, treeLock() and children for reading.
    12301237 */
    12311238HRESULT HardDisk2::saveSettings (settings::Key &aParentNode)
     
    12391246
    12401247    AutoReadLock alock (this);
     1248
     1249    /* we access mParent */
     1250    AutoReadLock treeLock (this->treeLock());
    12411251
    12421252    Key diskNode = aParentNode.appendKey ("HardDisk");
     
    14021412 *                      if no real merge is necessary).
    14031413 *
    1404  * @note Locks the branch lock for reading. Locks this object, aTarget and all
     1414 * @note Locks treeLock() for reading. Locks this object, aTarget and all
    14051415 *       intermediate hard disks for writing.
    14061416 */
     
    14131423
    14141424    AutoWriteLock alock (this);
     1425
     1426    /* we access mParent & children() */
     1427    AutoReadLock treeLock (this->treeLock());
    14151428
    14161429    AssertReturn (mm.type == HardDiskType_Normal, E_FAIL);
     
    15281541 *                      no real merge takes place).
    15291542 *
    1530  * @note Locks the branch lock for writing. Locks the hard disks from the chain
    1531  *       for writing. Locks the machine object when the backward merge takes
    1532  *       place.
     1543 * @note Locks the hard disks from the chain for writing. Locks the machine
     1544 *       object when the backward merge takes place. Locks treeLock() lock for
     1545 *       reading or writing.
    15331546 */
    15341547HRESULT HardDisk2::discard (ComObjPtr <Progress> &aProgress, MergeChain *aChain)
     
    15501563        {
    15511564            AutoWriteLock alock (this);
     1565
     1566            /* we access mParent & children() */
     1567            AutoReadLock treeLock (this->treeLock());
    15521568
    15531569            Assert (children().size() == 0);
     
    15991615 *                      no real merge takes place).
    16001616 *
    1601  * @note Locks the hard disks from the chain for writing.
     1617 * @note Locks the hard disks from the chain for writing. Locks treeLock() for
     1618 *       reading.
    16021619 */
    16031620void HardDisk2::cancelDiscard (MergeChain *aChain)
     
    16091626    {
    16101627        AutoWriteLock alock (this);
     1628
     1629        /* we access mParent & children() */
     1630        AutoReadLock treeLock (this->treeLock());
    16111631
    16121632        Assert (children().size() == 0);
     
    16561676 *                      an asynchronous thread.
    16571677 *
    1658  * @note Locks mVirtualBox, mParent and this object for writing.
     1678 * @note Locks mVirtualBox and this object for writing. Locks treeLock() for
     1679 *       writing.
    16591680 */
    16601681HRESULT HardDisk2::deleteStorage (ComObjPtr <Progress> *aProgress, bool aWait)
    16611682{
    16621683    AssertReturn (aProgress != NULL || aWait == true, E_FAIL);
    1663 
    1664     AutoCaller autoCaller (this);
    1665     CheckComRCReturnRC (autoCaller.rc());
    16661684
    16671685    /* unregisterWithVirtualBox() needs a write lock. We want to unregister
     
    16691687     * sure that we don't do that after another thread has done
    16701688     * VirtualBox::findHardDisk2() but before it starts using us (provided that
    1671      * it holds a mVirtualBox lock too of course).
    1672      *
    1673      * mParent->removeDependentChild() needs a write lock too. */
    1674 
    1675     AutoMultiWriteLock3 alock (mVirtualBox, mParent, this);
     1689     * it holds a mVirtualBox lock too of course). */
     1690
     1691    AutoWriteLock vboxLock (mVirtualBox);
     1692
     1693    AutoWriteLock alock (this);
    16761694
    16771695    switch (m.state)
     
    16911709    CheckComRCReturnRC (rc);
    16921710
     1711    /* go to Deleting state before leaving the lock */
     1712    m.state = MediaState_Deleting;
     1713
     1714    /* we need to leave this object's write lock now because of
     1715     * unregisterWithVirtualBox() that locks treeLock() for writing */
     1716    alock.leave();
     1717
    16931718    /* try to remove from the list of known hard disks before performing actual
    16941719     * deletion (we favor the consistency of the media registry in the first
     
    16971722
    16981723    rc = unregisterWithVirtualBox();
     1724
     1725    alock.enter();
     1726
     1727    /* restore the state because we may fail below; we will set it later again*/
     1728    m.state = MediaState_Created;
     1729
    16991730    CheckComRCReturnRC (rc);
    17001731
     
    19161947 *                              hard disk is attached to any VM.
    19171948 *
    1918  * @note Locks the branch lock for reading. Locks this object, aTarget and all
     1949 * @note Locks treeLock() for reading. Locks this object, aTarget and all
    19191950 *       intermediate hard disks for writing.
    19201951 */
     
    19281959    AssertComRCReturnRC (autoCaller.rc());
    19291960
    1930     AutoCaller targetCaller (this);
     1961    AutoCaller targetCaller (aTarget);
    19311962    AssertComRCReturnRC (targetCaller.rc());
    19321963
    19331964    aChain = NULL;
    19341965
    1935     /* tree lock is always the first */
     1966    /* we walk the tree */
    19361967    AutoReadLock treeLock (this->treeLock());
    19371968
     
    19561987            {
    19571988                Bstr tgtLoc;
    1958                 rc = aTarget->COMGETTER(Location) (tgtLoc.asOutParam());
    1959                 CheckComRCThrowRC (rc);
     1989                {
     1990                    AutoReadLock alock (this);
     1991                    tgtLoc = aTarget->locationFull();
     1992                }
    19601993
    19611994                AutoReadLock alock (this);
     
    22482281 *       accessibility.
    22492282 *
    2250  * @note Locks this object for writing.
     2283 * @note Locks treeLock() for reading and writing (for new diff media checked
     2284 *       for the first time). Locks mParent for reading. Locks this object for
     2285 *       writing.
    22512286 */
    22522287HRESULT HardDisk2::queryInfo()
     
    24132448                    }
    24142449
    2415                     /* associate with parent, deassociate from VirtualBox */
     2450                    /* deassociate from VirtualBox, associate with parent */
     2451
     2452                    mVirtualBox->removeDependentChild (this);
     2453
     2454                    /* we set mParent & children() */
     2455                    AutoWriteLock treeLock (this->treeLock());
     2456
    24162457                    Assert (mParent.isNull());
    24172458                    mParent = parent;
    24182459                    mParent->addDependentChild (this);
    2419                     mVirtualBox->removeDependentChild (this);
    24202460                }
    24212461                else
    24222462                {
     2463                    /* we access mParent */
     2464                    AutoReadLock treeLock (this->treeLock());
     2465
    24232466                    /* check that parent UUIDs match. Note that there's no need
    24242467                     * for the parent's AutoCaller (our lifetime is bound to
     
    25142557 * @note Called from this object's AutoMayUninitSpan and from under mVirtualBox
    25152558 *       write lock.
     2559 *
     2560 * @note Locks treeLock() for reading.
    25162561 */
    25172562HRESULT HardDisk2::canClose()
    25182563{
     2564    /* we access children */
     2565    AutoReadLock treeLock (this->treeLock());
     2566
    25192567    if (children().size() != 0)
    25202568        return setError (E_FAIL,
     
    25442592 *       from under mVirtualBox write lock.
    25452593 *
    2546  * @note Locks mParent for writing.
     2594 * @note Locks treeLock() for writing.
    25472595 */
    25482596HRESULT HardDisk2::unregisterWithVirtualBox()
     
    25512599     * unregisterHardDisk2() properly save the registry */
    25522600
     2601    /* we modify mParent and access children */
     2602    AutoWriteLock treeLock (this->treeLock());
     2603
    25532604    const ComObjPtr <HardDisk2, ComWeakRef> parent = mParent;
    2554 
    2555     /* Lock parent to make the try atomic WRT to mParent->COMGETTER(Children) */
    2556     AutoWriteLock parentLock (parent);
    25572605
    25582606    AssertReturn (children().size() == 0, E_FAIL);
     
    28802928            if (SUCCEEDED (rc))
    28812929            {
    2882                 /* mVirtualBox->registerHardDisk2() needs a write lock */
    2883                 AutoWriteLock vboxLock (that->mVirtualBox);
    2884                 thatLock.enter();
    2885 
    2886                 target->m.size = size;
    2887                 target->mm.logicalSize = logicalSize;
     2930                /* we set mParent & children() (note that thatLock is released
     2931                 * here), but lock VirtualBox first to follow the rule */
     2932                AutoMultiWriteLock2 alock (that->mVirtualBox->lockHandle(),
     2933                                           that->treeLock());
    28882934
    28892935                Assert (target->mParent.isNull());
     
    29002946                rc = that->mVirtualBox->registerHardDisk2 (target);
    29012947
    2902                 if (SUCCEEDED (rc))
    2903                 {
    2904                     target->m.state = MediaState_Created;
    2905                 }
    2906                 else
     2948                if (FAILED (rc))
    29072949                {
    29082950                    /* break the parent association on failure to register */
     
    29132955            }
    29142956
    2915             if (FAILED (rc))
     2957            thatLock.maybeEnter();
     2958
     2959            if (SUCCEEDED (rc))
    29162960            {
    2917                 thatLock.maybeEnter();
    2918 
     2961                target->m.state = MediaState_Created;
     2962
     2963                target->m.size = size;
     2964                target->mm.logicalSize = logicalSize;
     2965            }
     2966            else
     2967            {
    29192968                /* back to NotCreated on failiure */
    29202969                target->m.state = MediaState_NotCreated;
     
    29803029                        /* complex sanity (sane complexity) */
    29813030                        Assert ((chain->isForward() &&
    2982                                  (*it != chain->back() &&
    2983                                   (*it)->m.state == MediaState_Deleting) ||
    2984                                  (*it == chain->back() &&
    2985                                   (*it)->m.state == MediaState_LockedWrite)) ||
     3031                                 ((*it != chain->back() &&
     3032                                   (*it)->m.state == MediaState_Deleting) ||
     3033                                  (*it == chain->back() &&
     3034                                   (*it)->m.state == MediaState_LockedWrite))) ||
    29863035                                (!chain->isForward() &&
    2987                                  (*it != chain->front() &&
    2988                                   (*it)->m.state == MediaState_Deleting) ||
    2989                                  (*it == chain->front() &&
    2990                                   (*it)->m.state == MediaState_LockedWrite)));
     3036                                 ((*it != chain->front() &&
     3037                                   (*it)->m.state == MediaState_Deleting) ||
     3038                                  (*it == chain->front() &&
     3039                                   (*it)->m.state == MediaState_LockedWrite))));
    29913040
    29923041                        Assert (*it == chain->target() ||
     
    30883137                 * VDMerge; reparent the last one and uninitialize deleted */
    30893138
    3090                 /* mVirtualBox->(un)registerHardDisk2() needs a write lock */
    3091                 AutoWriteLock vboxLock (that->mVirtualBox);
    3092 
    3093                 /* we will reparent */
    3094                 AutoWriteLock treeLock (that->mVirtualBox->hardDiskTreeHandle());
     3139                /* we set mParent & children() (note that thatLock is released
     3140                 * here), but lock VirtualBox first to follow the rule */
     3141                AutoMultiWriteLock2 alock (that->mVirtualBox->lockHandle(),
     3142                                           that->treeLock());
    30953143
    30963144                HardDisk2 *source = chain->source();
     
    31043152                        unregisterHardDisk2 (target, false /* aSaveSettings */);
    31053153                    AssertComRC (rc2);
    3106 
    3107                     /* obey {parent,child} lock order (add/remove methods below
    3108                      * do locking) */
    3109                     AutoWriteLock parentLock (chain->parent());
    3110                     AutoWriteLock sourceLock (source);
    3111                     AutoWriteLock targetLock (target);
    31123154
    31133155                    /* then, reparent it and disconnect the deleted branch at
     
    31343176                else
    31353177                {
    3136                     /* obey {parent,child} lock order (add/remove methods below
    3137                      * do locking) */
    3138                     AutoWriteLock targetLock (target);
    3139 
    31403178                    Assert (target->children().size() == 1);
    31413179                    HardDisk2 *targetChild = target->children().front();
    3142 
    3143                     AutoWriteLock targetChildLock (target);
    31443180
    31453181                    /* disconnect the deleted branch at the elder end */
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