VirtualBox

Ignore:
Timestamp:
Feb 27, 2012 9:25:12 AM (13 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
76471
Message:

Main/Medium: rework locking scheme to solve lock order violations and long GUI start up time caused by too much locking
Main/all: Remove the enter and leave methods from write locks, they cause hard to find locking problems. Better solve them explicitly.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/glue/AutoLock.cpp

    r38773 r40257  
    55
    66/*
    7  * Copyright (C) 2006-2011 Oracle Corporation
     7 * Copyright (C) 2006-2012 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    7171        { LOCKCLASS_MACHINEOBJECT,      "5-MACHINEOBJECT" },
    7272        { LOCKCLASS_SNAPSHOTOBJECT,     "6-SNAPSHOTOBJECT" },
    73         { LOCKCLASS_LISTOFMEDIA,        "7-LISTOFMEDIA" },
    74         { LOCKCLASS_LISTOFOTHEROBJECTS, "8-LISTOFOTHEROBJECTS" },
    75         { LOCKCLASS_MEDIUMQUERY,        "9-MEDIUMQUERY" },
     73        { LOCKCLASS_MEDIUMQUERY,        "7-MEDIUMQUERY" },
     74        { LOCKCLASS_LISTOFMEDIA,        "8-LISTOFMEDIA" },
     75        { LOCKCLASS_LISTOFOTHEROBJECTS, "9-LISTOFOTHEROBJECTS" },
    7676        { LOCKCLASS_OTHEROBJECT,        "10-OTHEROBJECT" },
    7777        { LOCKCLASS_USBLIST,            "11-USBLIST" },
     
    303303
    304304typedef std::vector<LockHandle*> HandlesVector;
    305 typedef std::vector<uint32_t> CountsVector;
    306305
    307306struct AutoLockBase::Data
     
    315314        )
    316315        : fIsLocked(false),
    317           aHandles(cHandles),       // size of array
    318           acUnlockedInLeave(cHandles)
     316          aHandles(cHandles)        // size of array
    319317#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
    320318          , pcszFile(pcszFile_),
     
    324322    {
    325323        for (uint32_t i = 0; i < cHandles; ++i)
    326         {
    327             acUnlockedInLeave[i] = 0;
    328324            aHandles[i] = NULL;
    329         }
    330325    }
    331326
     
    335330                                        // and AutoReadLock, there will only be one item on the list; with the
    336331                                        // AutoMulti* derivatives, there will be multiple
    337     CountsVector    acUnlockedInLeave;  // for each lock handle, how many times the handle was unlocked in leave(); otherwise 0
    338332
    339333#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
     
    415409 * Destructor implementation that can also be called explicitly, if required.
    416410 * Restores the exact state before the AutoLock was created; that is, unlocks
    417  * all contained semaphores and might actually lock them again if leave()
    418  * was called during the AutoLock's lifetime.
     411 * all contained semaphores.
    419412 */
    420413void AutoLockBase::cleanup()
    421414{
    422     bool fAnyUnlockedInLeave = false;
    423 
    424     uint32_t i = 0;
    425     for (HandlesVector::iterator it = m->aHandles.begin();
    426          it != m->aHandles.end();
    427          ++it)
    428     {
    429         LockHandle *pHandle = *it;
    430         if (pHandle)
    431         {
    432             if (m->acUnlockedInLeave[i])
    433             {
    434                 // there was a leave() before the destruction: then restore the
    435                 // lock level that might have been set by locks other than our own
    436                 if (m->fIsLocked)
    437                 {
    438                     --m->acUnlockedInLeave[i];
    439                     fAnyUnlockedInLeave = true;
    440                 }
    441                 for (; m->acUnlockedInLeave[i]; --m->acUnlockedInLeave[i])
    442                     callLockImpl(*pHandle);
    443             }
    444         }
    445         ++i;
    446     }
    447 
    448     if (m->fIsLocked && !fAnyUnlockedInLeave)
     415    if (m->fIsLocked)
    449416        callUnlockOnAllHandles();
    450417}
     
    553520{
    554521    l.unlockWrite();
    555 }
    556 
    557 /**
    558  * Causes the current thread to completely release the write lock to make
    559  * the managed semaphore immediately available for locking by other threads.
    560  *
    561  * This implies that all nested write locks on the semaphore will be
    562  * released, even those that were acquired through the calls to #lock()
    563  * methods of all other AutoWriteLock/AutoReadLock instances managing the
    564  * <b>same</b> read/write semaphore.
    565  *
    566  * After calling this method, the only method you are allowed to call is
    567  * #enter(). It will acquire the write lock again and restore the same
    568  * level of nesting as it had before calling #leave().
    569  *
    570  * If this instance is destroyed without calling #enter(), the destructor
    571  * will try to restore the write lock level that existed when #leave() was
    572  * called minus the number of nested #lock() calls made on this instance
    573  * itself. This is done to preserve lock levels of other
    574  * AutoWriteLock/AutoReadLock instances managing the same semaphore (if
    575  * any). Tiis also means that the destructor may indefinitely block if a
    576  * write or a read lock is owned by some other thread by that time.
    577  */
    578 void AutoWriteLockBase::leave()
    579 {
    580     AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot leave()!"));
    581 
    582     // unlock in reverse order!
    583     uint32_t i = m->aHandles.size();
    584     for (HandlesVector::reverse_iterator it = m->aHandles.rbegin();
    585          it != m->aHandles.rend();
    586          ++it)
    587     {
    588         --i;            // array index is zero based, decrement with every loop since we iterate backwards
    589         LockHandle *pHandle = *it;
    590         if (pHandle)
    591         {
    592             AssertMsg(m->acUnlockedInLeave[i] == 0, ("m->cUnlockedInLeave[%d] is %d, must be 0! Called leave() twice?", i, m->acUnlockedInLeave[i]));
    593             m->acUnlockedInLeave[i] = pHandle->writeLockLevel();
    594             AssertMsg(m->acUnlockedInLeave[i] >= 1, ("m->cUnlockedInLeave[%d] is %d, must be >=1!", i, m->acUnlockedInLeave[i]));
    595 
    596             for (uint32_t left = m->acUnlockedInLeave[i];
    597                  left;
    598                  --left)
    599                 callUnlockImpl(*pHandle);
    600         }
    601     }
    602 }
    603 
    604 /**
    605  * Causes the current thread to restore the write lock level after the
    606  * #leave() call. This call will indefinitely block if another thread has
    607  * successfully acquired a write or a read lock on the same semaphore in
    608  * between.
    609  */
    610 void AutoWriteLockBase::enter()
    611 {
    612     AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot enter()!"));
    613 
    614     uint32_t i = 0;
    615     for (HandlesVector::iterator it = m->aHandles.begin();
    616          it != m->aHandles.end();
    617          ++it)
    618     {
    619         LockHandle *pHandle = *it;
    620         if (pHandle)
    621         {
    622             AssertMsg(m->acUnlockedInLeave[i] != 0, ("m->cUnlockedInLeave[%d] is 0! enter() without leave()?", i));
    623 
    624             for (; m->acUnlockedInLeave[i]; --m->acUnlockedInLeave[i])
    625                 callLockImpl(*pHandle);
    626         }
    627         ++i;
    628     }
    629522}
    630523
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