VirtualBox

Changeset 25281 in vbox


Ignore:
Timestamp:
Dec 9, 2009 6:41:39 PM (15 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
55825
Message:

Main: preparation for deadlock detection: make AutoLockBase data private, move from header

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

Legend:

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

    r25279 r25281  
    8888}
    8989
     90////////////////////////////////////////////////////////////////////////////////
     91//
     92// AutoLockBase
     93//
     94////////////////////////////////////////////////////////////////////////////////
     95
     96struct AutoLockBase::Data
     97{
     98    Data(LockHandle *argpHandle)
     99        : pHandle(argpHandle),
     100          fIsLocked(false),
     101          cUnlockedInLeave(0)
     102    { }
     103
     104    LockHandle      *pHandle;
     105    bool            fIsLocked;
     106    uint32_t        cUnlockedInLeave;   // how many times the handle was unlocked in leave(); otherwise 0
     107};
     108
     109AutoLockBase::AutoLockBase(LockHandle *pHandle)
     110{
     111    m = new Data(pHandle);
     112}
     113
     114AutoLockBase::~AutoLockBase()
     115{
     116    delete m;
     117}
     118
     119////////////////////////////////////////////////////////////////////////////////
     120//
     121// AutoWriteLock
     122//
     123////////////////////////////////////////////////////////////////////////////////
     124
     125void AutoWriteLock::cleanup()
     126{
     127    if (m->pHandle)
     128    {
     129        if (m->cUnlockedInLeave)
     130        {
     131            // there was a leave() before the destruction: then restore the
     132            // lock level that might have been set by locks other than our own
     133            if (m->fIsLocked)
     134                --m->cUnlockedInLeave;       // no lock for our own
     135            m->fIsLocked = false;
     136            for (; m->cUnlockedInLeave; --m->cUnlockedInLeave)
     137                m->pHandle->lockWrite();
     138
     139                // @todo r=dj is this really desirable behavior? maybe leave/enter should go altogether?
     140        }
     141
     142        if (m->fIsLocked)
     143            m->pHandle->unlockWrite();
     144    }
     145}
     146
     147void AutoWriteLock::acquire()
     148{
     149    if (m->pHandle)
     150    {
     151        AssertMsg(!m->fIsLocked, ("m->fIsLocked is true, attempting to lock twice!"));
     152        m->pHandle->lockWrite();
     153        m->fIsLocked = true;
     154    }
     155}
     156
     157void AutoWriteLock::release()
     158{
     159    if (m->pHandle)
     160    {
     161        AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot release!"));
     162        m->pHandle->unlockWrite();
     163        m->fIsLocked = false;
     164    }
     165}
     166
     167/**
     168 * Causes the current thread to completely release the write lock to make
     169 * the managed semaphore immediately available for locking by other threads.
     170 *
     171 * This implies that all nested write locks on the semaphore will be
     172 * released, even those that were acquired through the calls to #lock()
     173 * methods of all other AutoWriteLock/AutoReadLock instances managing the
     174 * <b>same</b> read/write semaphore.
     175 *
     176 * After calling this method, the only method you are allowed to call is
     177 * #enter(). It will acquire the write lock again and restore the same
     178 * level of nesting as it had before calling #leave().
     179 *
     180 * If this instance is destroyed without calling #enter(), the destructor
     181 * will try to restore the write lock level that existed when #leave() was
     182 * called minus the number of nested #lock() calls made on this instance
     183 * itself. This is done to preserve lock levels of other
     184 * AutoWriteLock/AutoReadLock instances managing the same semaphore (if
     185 * any). Tiis also means that the destructor may indefinitely block if a
     186 * write or a read lock is owned by some other thread by that time.
     187 */
     188void AutoWriteLock::leave()
     189{
     190    if (m->pHandle)
     191    {
     192        AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot leave()!"));
     193        AssertMsg(m->cUnlockedInLeave == 0, ("m->cUnlockedInLeave is %d, must be 0! Called leave() twice?", m->cUnlockedInLeave));
     194
     195        m->cUnlockedInLeave = m->pHandle->writeLockLevel();
     196        AssertMsg(m->cUnlockedInLeave >= 1, ("m->cUnlockedInLeave is %d, must be >=1!", m->cUnlockedInLeave));
     197
     198        for (uint32_t left = m->cUnlockedInLeave;
     199                left;
     200                --left)
     201            m->pHandle->unlockWrite();
     202    }
     203}
     204
     205/**
     206 * Causes the current thread to restore the write lock level after the
     207 * #leave() call. This call will indefinitely block if another thread has
     208 * successfully acquired a write or a read lock on the same semaphore in
     209 * between.
     210 */
     211void AutoWriteLock::enter()
     212{
     213    if (m->pHandle)
     214    {
     215        AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot enter()!"));
     216        AssertMsg(m->cUnlockedInLeave != 0, ("m->cUnlockedInLeave is 0! enter() without leave()?"));
     217
     218        for (; m->cUnlockedInLeave; --m->cUnlockedInLeave)
     219            m->pHandle->lockWrite();
     220    }
     221}
     222
     223/**
     224 * Attaches another handle to this auto lock instance.
     225 *
     226 * The previous object's lock is completely released before the new one is
     227 * acquired. The lock level of the new handle will be the same. This
     228 * also means that if the lock was not acquired at all before #attach(), it
     229 * will not be acquired on the new handle too.
     230 *
     231 * @param aHandle   New handle to attach.
     232 */
     233void AutoWriteLock::attach(LockHandle *aHandle)
     234{
     235    /* detect simple self-reattachment */
     236    if (m->pHandle != aHandle)
     237    {
     238        bool fWasLocked = m->fIsLocked;
     239
     240        cleanup();
     241
     242        m->pHandle = aHandle;
     243        m->fIsLocked = fWasLocked;
     244
     245        if (m->pHandle)
     246            if (fWasLocked)
     247                m->pHandle->lockWrite();
     248    }
     249}
     250
     251void AutoWriteLock::attachRaw(LockHandle *ph)
     252{
     253    m->pHandle = ph;
     254}
     255
     256/**
     257 * Returns @c true if the current thread holds a write lock on the managed
     258 * read/write semaphore. Returns @c false if the managed semaphore is @c
     259 * NULL.
     260 *
     261 * @note Intended for debugging only.
     262 */
     263bool AutoWriteLock::isWriteLockOnCurrentThread() const
     264{
     265    return m->pHandle ? m->pHandle->isWriteLockOnCurrentThread() : false;
     266}
     267
     268 /**
     269 * Returns the current write lock level of the managed smaphore. The lock
     270 * level determines the number of nested #lock() calls on the given
     271 * semaphore handle. Returns @c 0 if the managed semaphore is @c
     272 * NULL.
     273 *
     274 * Note that this call is valid only when the current thread owns a write
     275 * lock on the given semaphore handle and will assert otherwise.
     276 *
     277 * @note Intended for debugging only.
     278 */
     279uint32_t AutoWriteLock::writeLockLevel() const
     280{
     281    return m->pHandle ? m->pHandle->writeLockLevel() : 0;
     282}
     283
     284////////////////////////////////////////////////////////////////////////////////
     285//
     286// AutoReadLock
     287//
     288////////////////////////////////////////////////////////////////////////////////
     289
     290/**
     291 * Release all read locks acquired by this instance through the #lock()
     292 * call and destroys the instance.
     293 *
     294 * Note that if there there are nested #lock() calls without the
     295 * corresponding number of #unlock() calls when the destructor is called, it
     296 * will assert. This is because having an unbalanced number of nested locks
     297 * is a program logic error which must be fixed.
     298 */
     299/*virtual*/ AutoReadLock::~AutoReadLock()
     300{
     301    if (m->pHandle)
     302    {
     303        if (m->fIsLocked)
     304            m->pHandle->unlockRead();
     305    }
     306}
     307
     308/**
     309 * Requests a read (shared) lock. If a read lock is already owned by
     310 * this thread, increases the lock level (allowing for nested read locks on
     311 * the same thread). Blocks indefinitely if a write lock is already owned by
     312 * another thread until that tread releases the write lock, otherwise
     313 * returns immediately.
     314 *
     315 * Note that this method returns immediately even if any number of other
     316 * threads owns read locks on the same semaphore. Also returns immediately
     317 * if a write lock on this semaphore is owned by the current thread which
     318 * allows for read locks nested into write locks on the same thread.
     319 */
     320void AutoReadLock::acquire()
     321{
     322    if (m->pHandle)
     323    {
     324        AssertMsg(!m->fIsLocked, ("m->fIsLocked is true, attempting to lock twice!"));
     325        m->pHandle->lockRead();
     326        m->fIsLocked = true;
     327    }
     328}
     329
     330/**
     331 * Decreases the read lock level increased by #lock(). If the level drops to
     332 * zero (e.g. the number of nested #unlock() calls matches the number of
     333 * nested #lock() calls), releases the lock making the managed semaphore
     334 * available for locking by other threads.
     335 */
     336void AutoReadLock::release()
     337{
     338    if (m->pHandle)
     339    {
     340        AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot release!"));
     341        m->pHandle->unlockRead();
     342        m->fIsLocked = false;
     343    }
     344}
     345
     346
    90347} /* namespace util */
    91348/* vi: set tabstop=4 shiftwidth=4 expandtab: */
  • trunk/src/VBox/Main/include/AutoLock.h

    r25280 r25281  
    317317{
    318318protected:
    319     AutoLockBase(LockHandle *pHandle)
    320         : mHandle(pHandle),
    321           m_fIsLocked(false),
    322           m_cUnlockedInLeave(0)
    323     { }
    324 
    325     LockHandle      *mHandle;
    326     bool            m_fIsLocked;
    327     uint32_t        m_cUnlockedInLeave;   // how many times the handle was unlocked in leave(); otherwise 0
     319    AutoLockBase(LockHandle *pHandle);
     320    virtual ~AutoLockBase();
     321
     322    struct Data;
     323    Data *m;
     324
     325private:
     326    // prohibit copy + assignment
     327    AutoLockBase(const AutoLockBase&);
     328    AutoLockBase& operator=(AutoLockBase&);
    328329};
    329330
     
    399400     * is a program logic error which must be fixed.
    400401     */
    401     ~AutoWriteLock()
     402    virtual ~AutoWriteLock()
    402403    {
    403404        cleanup();
    404405    }
    405406
    406     void cleanup()
    407     {
    408         if (mHandle)
    409         {
    410             if (m_cUnlockedInLeave)
    411             {
    412                 // there was a leave() before the destruction: then restore the
    413                 // lock level that might have been set by locks other than our own
    414                 if (m_fIsLocked)
    415                     --m_cUnlockedInLeave;       // no lock for our own
    416                 m_fIsLocked = false;
    417                 for (; m_cUnlockedInLeave; --m_cUnlockedInLeave)
    418                     mHandle->lockWrite();
    419 
    420                     // @todo r=dj is this really desirable behavior? maybe leave/enter should go altogether?
    421             }
    422 
    423             if (m_fIsLocked)
    424                 mHandle->unlockWrite();
    425         }
    426     }
    427 
    428     /**
    429      * Requests a write (exclusive) lock. If a write lock is already owned by
    430      * this thread, increases the lock level (allowing for nested write locks on
    431      * the same thread). Blocks indefinitely if a write lock or a read lock is
    432      * already owned by another thread until that tread releases the locks,
    433      * otherwise returns immediately.
    434      */
    435     void acquire()
    436     {
    437         if (mHandle)
    438         {
    439             AssertMsg(!m_fIsLocked, ("m_fIsLocked is true, attempting to lock twice!"));
    440             mHandle->lockWrite();
    441             m_fIsLocked = true;
    442         }
    443     }
    444 
    445     /**
    446      * Decreases the write lock level increased by #lock(). If the level drops
    447      * to zero (e.g. the number of nested #unlock() calls matches the number of
    448      * nested #lock() calls), releases the lock making the managed semaphore
    449      * available for locking by other threads.
    450      */
    451     void release()
    452     {
    453         if (mHandle)
    454         {
    455             AssertMsg(m_fIsLocked, ("m_fIsLocked is false, cannot release!"));
    456             mHandle->unlockWrite();
    457             m_fIsLocked = false;
    458         }
    459     }
    460 
    461     /**
    462      * Causes the current thread to completely release the write lock to make
    463      * the managed semaphore immediately available for locking by other threads.
    464      *
    465      * This implies that all nested write locks on the semaphore will be
    466      * released, even those that were acquired through the calls to #lock()
    467      * methods of all other AutoWriteLock/AutoReadLock instances managing the
    468      * <b>same</b> read/write semaphore.
    469      *
    470      * After calling this method, the only method you are allowed to call is
    471      * #enter(). It will acquire the write lock again and restore the same
    472      * level of nesting as it had before calling #leave().
    473      *
    474      * If this instance is destroyed without calling #enter(), the destructor
    475      * will try to restore the write lock level that existed when #leave() was
    476      * called minus the number of nested #lock() calls made on this instance
    477      * itself. This is done to preserve lock levels of other
    478      * AutoWriteLock/AutoReadLock instances managing the same semaphore (if
    479      * any). Tiis also means that the destructor may indefinitely block if a
    480      * write or a read lock is owned by some other thread by that time.
    481      */
    482     void leave()
    483     {
    484         if (mHandle)
    485         {
    486             AssertMsg(m_fIsLocked, ("m_fIsLocked is false, cannot leave()!"));
    487             AssertMsg(m_cUnlockedInLeave == 0, ("m_cUnlockedInLeave is %d, must be 0! Called leave() twice?", m_cUnlockedInLeave));
    488 
    489             m_cUnlockedInLeave = mHandle->writeLockLevel();
    490             AssertMsg(m_cUnlockedInLeave >= 1, ("m_cUnlockedInLeave is %d, must be >=1!", m_cUnlockedInLeave));
    491 
    492             for (uint32_t left = m_cUnlockedInLeave;
    493                  left;
    494                  --left)
    495                 mHandle->unlockWrite();
    496         }
    497     }
     407    void cleanup();
     408
     409    void acquire();
     410    void release();
     411    void leave();
     412    void enter();
    498413
    499414    /**
     
    519434    }
    520435
    521     /**
    522      * Causes the current thread to restore the write lock level after the
    523      * #leave() call. This call will indefinitely block if another thread has
    524      * successfully acquired a write or a read lock on the same semaphore in
    525      * between.
    526      */
    527     void enter()
    528     {
    529         if (mHandle)
    530         {
    531             AssertMsg(m_fIsLocked, ("m_fIsLocked is false, cannot enter()!"));
    532             AssertMsg(m_cUnlockedInLeave != 0, ("m_cUnlockedInLeave is 0! enter() without leave()?"));
    533 
    534             for (; m_cUnlockedInLeave; --m_cUnlockedInLeave)
    535                 mHandle->lockWrite();
    536         }
    537     }
    538 
    539     /**
    540      * Attaches another handle to this auto lock instance.
    541      *
    542      * The previous object's lock is completely released before the new one is
    543      * acquired. The lock level of the new handle will be the same. This
    544      * also means that if the lock was not acquired at all before #attach(), it
    545      * will not be acquired on the new handle too.
    546      *
    547      * @param aHandle   New handle to attach.
    548      */
    549     void attach(LockHandle *aHandle)
    550     {
    551         /* detect simple self-reattachment */
    552         if (mHandle != aHandle)
    553         {
    554             bool fWasLocked = m_fIsLocked;
    555 
    556             cleanup();
    557 
    558             mHandle = aHandle;
    559             m_fIsLocked = fWasLocked;
    560 
    561             if (mHandle)
    562                 if (fWasLocked)
    563                     mHandle->lockWrite();
    564         }
    565     }
     436    void attach(LockHandle *aHandle);
    566437
    567438    /** @see attach (LockHandle *) */
     
    583454    }
    584455
    585     /**
    586      * Returns @c true if the current thread holds a write lock on the managed
    587      * read/write semaphore. Returns @c false if the managed semaphore is @c
    588      * NULL.
    589      *
    590      * @note Intended for debugging only.
    591      */
    592     bool isWriteLockOnCurrentThread() const
    593     {
    594         return mHandle ? mHandle->isWriteLockOnCurrentThread() : false;
    595     }
    596 
    597     /**
    598      * Returns the current write lock level of the managed smaphore. The lock
    599      * level determines the number of nested #lock() calls on the given
    600      * semaphore handle. Returns @c 0 if the managed semaphore is @c
    601      * NULL.
    602      *
    603      * Note that this call is valid only when the current thread owns a write
    604      * lock on the given semaphore handle and will assert otherwise.
    605      *
    606      * @note Intended for debugging only.
    607      */
    608     uint32_t writeLockLevel() const
    609     {
    610         return mHandle ? mHandle->writeLockLevel() : 0;
    611     }
    612 
    613 private:
    614 
    615     DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoWriteLock)
    616     DECLARE_CLS_NEW_DELETE_NOOP (AutoWriteLock)
    617 
    618     template <size_t> friend class AutoMultiWriteLockBase;
     456    void attachRaw(LockHandle *ph);
     457
     458    bool isWriteLockOnCurrentThread() const;
     459    uint32_t writeLockLevel() const;
    619460};
    620461
     
    681522    }
    682523
    683     /**
    684      * Release all read locks acquired by this instance through the #lock()
    685      * call and destroys the instance.
    686      *
    687      * Note that if there there are nested #lock() calls without the
    688      * corresponding number of #unlock() calls when the destructor is called, it
    689      * will assert. This is because having an unbalanced number of nested locks
    690      * is a program logic error which must be fixed.
    691      */
    692     ~AutoReadLock()
    693     {
    694         if (mHandle)
    695         {
    696             if (m_fIsLocked)
    697                 mHandle->unlockRead();
    698         }
    699     }
    700 
    701     /**
    702      * Requests a read (shared) lock. If a read lock is already owned by
    703      * this thread, increases the lock level (allowing for nested read locks on
    704      * the same thread). Blocks indefinitely if a write lock is already owned by
    705      * another thread until that tread releases the write lock, otherwise
    706      * returns immediately.
    707      *
    708      * Note that this method returns immediately even if any number of other
    709      * threads owns read locks on the same semaphore. Also returns immediately
    710      * if a write lock on this semaphore is owned by the current thread which
    711      * allows for read locks nested into write locks on the same thread.
    712      */
    713     void acquire()
    714     {
    715         if (mHandle)
    716         {
    717             AssertMsg(!m_fIsLocked, ("m_fIsLocked is true, attempting to lock twice!"));
    718             mHandle->lockRead();
    719             m_fIsLocked = true;
    720         }
    721     }
    722 
    723     /**
    724      * Decreases the read lock level increased by #lock(). If the level drops to
    725      * zero (e.g. the number of nested #unlock() calls matches the number of
    726      * nested #lock() calls), releases the lock making the managed semaphore
    727      * available for locking by other threads.
    728      */
    729     void release()
    730     {
    731         if (mHandle)
    732         {
    733             AssertMsg(m_fIsLocked, ("m_fIsLocked is false, cannot release!"));
    734             mHandle->unlockRead();
    735             m_fIsLocked = false;
    736         }
    737     }
    738 
    739 private:
    740 
    741     DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoReadLock)
    742     DECLARE_CLS_NEW_DELETE_NOOP (AutoReadLock)
     524    virtual ~AutoReadLock();
     525
     526    void acquire();
     527    void release();
    743528};
    744529
     
    982767    AutoMultiWriteLockBase() {}
    983768
    984     void setLockHandle (size_t aIdx, LockHandle *aHandle)
    985     { mLocks [aIdx].mHandle = aHandle; }
     769    void setLockHandle(size_t aIdx, LockHandle *aHandle)
     770    {
     771        mLocks[aIdx].attachRaw(aHandle);
     772    }
    986773
    987774private:
    988775
    989     AutoWriteLock mLocks [Cnt];
     776    AutoWriteLock mLocks[Cnt];
    990777
    991778    DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoMultiWriteLockBase)
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