Changeset 25281 in vbox
- Timestamp:
- Dec 9, 2009 6:41:39 PM (15 years ago)
- svn:sync-xref-src-repo-rev:
- 55825
- Location:
- trunk/src/VBox/Main
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/AutoLock.cpp
r25279 r25281 88 88 } 89 89 90 //////////////////////////////////////////////////////////////////////////////// 91 // 92 // AutoLockBase 93 // 94 //////////////////////////////////////////////////////////////////////////////// 95 96 struct 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 109 AutoLockBase::AutoLockBase(LockHandle *pHandle) 110 { 111 m = new Data(pHandle); 112 } 113 114 AutoLockBase::~AutoLockBase() 115 { 116 delete m; 117 } 118 119 //////////////////////////////////////////////////////////////////////////////// 120 // 121 // AutoWriteLock 122 // 123 //////////////////////////////////////////////////////////////////////////////// 124 125 void 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 147 void 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 157 void 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 */ 188 void 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 */ 211 void 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 */ 233 void 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 251 void 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 */ 263 bool 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 */ 279 uint32_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 */ 320 void 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 */ 336 void 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 90 347 } /* namespace util */ 91 348 /* vi: set tabstop=4 shiftwidth=4 expandtab: */ -
trunk/src/VBox/Main/include/AutoLock.h
r25280 r25281 317 317 { 318 318 protected: 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 325 private: 326 // prohibit copy + assignment 327 AutoLockBase(const AutoLockBase&); 328 AutoLockBase& operator=(AutoLockBase&); 328 329 }; 329 330 … … 399 400 * is a program logic error which must be fixed. 400 401 */ 401 ~AutoWriteLock()402 virtual ~AutoWriteLock() 402 403 { 403 404 cleanup(); 404 405 } 405 406 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(); 498 413 499 414 /** … … 519 434 } 520 435 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); 566 437 567 438 /** @see attach (LockHandle *) */ … … 583 454 } 584 455 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; 619 460 }; 620 461 … … 681 522 } 682 523 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(); 743 528 }; 744 529 … … 982 767 AutoMultiWriteLockBase() {} 983 768 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 } 986 773 987 774 private: 988 775 989 AutoWriteLock mLocks 776 AutoWriteLock mLocks[Cnt]; 990 777 991 778 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoMultiWriteLockBase)
Note:
See TracChangeset
for help on using the changeset viewer.