VirtualBox

source: vbox/trunk/src/VBox/Main/AutoLock.cpp@ 25414

Last change on this file since 25414 was 25408, checked in by vboxsync, 14 years ago

Main/Locking: fix regression in AutoMultiWriteLock that caused confusion in how many times locks needed to be unlocked; this caused lock validation to fail in restoreSnapshot(); also don't require unlocking to happen in exactly the reverse order of locking; documentation

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.3 KB
Line 
1/** @file
2 *
3 * Automatic locks, implementation
4 */
5
6/*
7 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#include "AutoLock.h"
23
24#include "Logging.h"
25
26#include <iprt/cdefs.h>
27#include <iprt/critsect.h>
28#include <iprt/thread.h>
29#include <iprt/semaphore.h>
30
31#include <iprt/err.h>
32#include <iprt/assert.h>
33
34#if defined(DEBUG)
35# include <iprt/asm.h> // for ASMReturnAddress
36#endif
37
38#include <iprt/string.h>
39#include <iprt/path.h>
40
41#include <VBox/com/string.h>
42
43#include <vector>
44#include <list>
45
46namespace util
47{
48
49////////////////////////////////////////////////////////////////////////////////
50//
51// Per-thread stacks for locking validation
52//
53////////////////////////////////////////////////////////////////////////////////
54
55#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
56// index used for allocating thread-local storage for locking stack
57RTTLS LockHandle::s_lockingStackTlsIndex = NIL_RTTLS;
58
59/**
60 * One item on the LockingStack. One of these gets pushed on the
61 * stack for each lock operation and popped for each unlock.
62 */
63struct LockStackItem
64{
65 LockStackItem(LockHandle *pLock_,
66 const char *pcszFile_,
67 unsigned uLine_,
68 const char *pcszFunction_)
69 : pLock(pLock_),
70 pcszFile(pcszFile_),
71 uLine(uLine_),
72 pcszFunction(pcszFunction_)
73 {
74 pcszFile = RTPathFilename(pcszFile_);
75 }
76
77 LockHandle *pLock;
78
79 // information about where the lock occured (passed down from the AutoLock classes)
80 const char *pcszFile;
81 unsigned uLine;
82 const char *pcszFunction;
83};
84
85typedef std::list<LockStackItem> LockHandlesList;
86
87/**
88 * LockingStack class. One of these gets created for each thread
89 * that calls lock/unlock methods, and a pointer to this is
90 * stored in thread-local storage.
91 */
92struct LockingStack
93{
94 LockingStack()
95 : threadSelf(RTThreadSelf()),
96 pcszThreadName(NULL),
97 c(0)
98 {
99 threadSelf = RTThreadSelf();
100 pcszThreadName = RTThreadGetName(threadSelf);
101 }
102
103 RTTHREAD threadSelf;
104 const char *pcszThreadName;
105
106 LockHandlesList ll;
107 // first item (front) is newest, last item (back) is oldest; I'd do it
108 // the other way round but there is no implementation for erase(reverse_iterator)
109 // which I'd need otherwise
110 size_t c;
111};
112
113/**
114 * Global helper that looks up the LockingStack structure for the
115 * current thread in thread-local storage, or creates one on the
116 * thread's first call.
117 */
118LockingStack* getThreadLocalLockingStack()
119{
120 // very first call in this process: allocate the TLS variable
121 if (LockHandle::s_lockingStackTlsIndex == NIL_RTTLS)
122 {
123 LockHandle::s_lockingStackTlsIndex = RTTlsAlloc();
124 Assert(LockHandle::s_lockingStackTlsIndex != NIL_RTTLS);
125 }
126
127 // get pointer to thread-local locking stack
128 LockingStack *pStack = (LockingStack*)RTTlsGet(LockHandle::s_lockingStackTlsIndex);
129 if (!pStack)
130 {
131 // first call on this thread:
132 pStack = new LockingStack;
133 RTTlsSet(LockHandle::s_lockingStackTlsIndex, pStack);
134 }
135
136 return pStack;
137}
138
139void dumpThreadLocalLockingStack(LockingStack *pStack)
140{
141 uint32_t c = 0;
142 for (LockHandlesList::iterator it = pStack->ll.begin();
143 it != pStack->ll.end();
144 ++it)
145 {
146 LockStackItem lsi = *it;
147 LogFlow(("LOCKVAL: lock %d under top is [%s] locked by %s (%s:%u)\n", c, lsi.pLock->describe(), lsi.pcszFunction, lsi.pcszFile, lsi.uLine));
148 ++c;
149 }
150}
151
152#endif
153
154////////////////////////////////////////////////////////////////////////////////
155//
156// LockHandle
157//
158////////////////////////////////////////////////////////////////////////////////
159
160#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
161
162/**
163 * If the lock validator is enabled, this gets called from the
164 * lock methods to push a LockStackItem onto the thread-local
165 * locking stack for tracing.
166 */
167void LockHandle::validateLock(LOCKVAL_SRC_POS_DECL)
168{
169 // put "this" on locking stack
170 LockingStack *pStack = getThreadLocalLockingStack();
171 LockStackItem lsi(this, RT_SRC_POS_ARGS);
172 pStack->ll.push_front(lsi);
173 ++pStack->c;
174
175 LogFlow(("LOCKVAL [%s]: lock from %s (%s:%u), new count: %RI32\n", describe(), lsi.pcszFunction, lsi.pcszFile, lsi.uLine, (uint32_t)pStack->c));
176}
177
178/**
179 * If the lock validator is enabled, this gets called from the
180 * unlock methods to validate the unlock request. This pops the
181 * LockStackItem from the thread-local locking stack.
182 */
183void LockHandle::validateUnlock()
184{
185 // pop "this" from locking stack
186 LockingStack *pStack = getThreadLocalLockingStack();
187
188 AssertMsg(pStack->c == pStack->ll.size(), ("Locking size mismatch"));
189 AssertMsg(pStack->c > 0, ("Locking stack is empty when it should have current LockHandle on top"));
190
191 // validate that "this" is the top item on the stack
192 LockStackItem &lsiTop = pStack->ll.front();
193 if (lsiTop.pLock != this)
194 {
195 // "this" was not the last to be locked on this thread;
196 // see if it's somewhere deep under the locks
197 dumpThreadLocalLockingStack(pStack);
198
199 bool fFound;
200 uint32_t c = 0;
201 for (LockHandlesList::iterator it = pStack->ll.begin();
202 it != pStack->ll.end();
203 ++it)
204 {
205 LockStackItem &lsiThis = *it;
206 if (lsiThis.pLock == this)
207 {
208 LogFlow(("LOCKVAL [%s]: unlock, stack item was %d items under the top item, corresponsing lock was at %s (%s:%u)\n", describe(), c, lsiThis.pcszFunction, lsiThis.pcszFile, lsiThis.uLine));
209 pStack->ll.erase(it);
210 fFound = true;
211 break;
212 }
213 ++c;
214 }
215
216 if (!fFound)
217 {
218 LogFlow(("LOCKVAL [%s]: unlock, stack item not found!\n", describe()));
219 AssertMsgFailed(("Locking stack does not contain current LockHandle at all\n"));
220 }
221 }
222 else
223 {
224 pStack->ll.pop_front();
225 LogFlow(("LOCKVAL [%s]: unlock, stack item was on top, old count: %RI32\n", describe(), (uint32_t)pStack->c));
226 }
227
228 --pStack->c;
229}
230
231#endif // VBOX_WITH_DEBUG_LOCK_VALIDATOR
232
233////////////////////////////////////////////////////////////////////////////////
234//
235// RWLockHandle
236//
237////////////////////////////////////////////////////////////////////////////////
238
239struct RWLockHandle::Data
240{
241 Data()
242 { }
243
244 RTSEMRW sem;
245
246#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
247 com::Utf8Str strDescription;
248#endif
249};
250
251RWLockHandle::RWLockHandle()
252{
253 m = new Data();
254 int vrc = RTSemRWCreate(&m->sem);
255 AssertRC(vrc);
256
257#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
258 m->strDescription = com::Utf8StrFmt("r/w %RCv", this);
259#endif
260}
261
262/*virtual*/ RWLockHandle::~RWLockHandle()
263{
264 RTSemRWDestroy(m->sem);
265 delete m;
266}
267
268/*virtual*/ bool RWLockHandle::isWriteLockOnCurrentThread() const
269{
270 return RTSemRWIsWriteOwner(m->sem);
271}
272
273/*virtual*/ void RWLockHandle::lockWrite(LOCKVAL_SRC_POS_DECL)
274{
275#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
276 validateLock(LOCKVAL_SRC_POS_ARGS);
277#endif
278 int vrc = RTSemRWRequestWrite(m->sem, RT_INDEFINITE_WAIT);
279 AssertRC(vrc);
280}
281
282/*virtual*/ void RWLockHandle::unlockWrite()
283{
284#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
285 validateUnlock();
286#endif
287 int vrc = RTSemRWReleaseWrite(m->sem);
288 AssertRC(vrc);
289
290}
291
292/*virtual*/ void RWLockHandle::lockRead(LOCKVAL_SRC_POS_DECL)
293{
294#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
295 validateLock(LOCKVAL_SRC_POS_ARGS);
296#endif
297 int vrc = RTSemRWRequestRead(m->sem, RT_INDEFINITE_WAIT);
298 AssertRC(vrc);
299}
300
301/*virtual*/ void RWLockHandle::unlockRead()
302{
303#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
304 validateUnlock();
305#endif
306 int vrc = RTSemRWReleaseRead(m->sem);
307 AssertRC(vrc);
308}
309
310/*virtual*/ uint32_t RWLockHandle::writeLockLevel() const
311{
312 return RTSemRWGetWriteRecursion(m->sem);
313}
314
315#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
316/*virtual*/ const char* RWLockHandle::describe() const
317{
318 return m->strDescription.c_str();
319}
320#endif
321
322////////////////////////////////////////////////////////////////////////////////
323//
324// WriteLockHandle
325//
326////////////////////////////////////////////////////////////////////////////////
327
328struct WriteLockHandle::Data
329{
330 Data()
331 { }
332
333 mutable RTCRITSECT sem;
334
335#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
336 com::Utf8Str strDescription;
337#endif
338};
339
340WriteLockHandle::WriteLockHandle()
341{
342 m = new Data;
343 RTCritSectInit(&m->sem);
344
345#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
346 m->strDescription = com::Utf8StrFmt("crit %RCv", this);
347#endif
348}
349
350WriteLockHandle::~WriteLockHandle()
351{
352 RTCritSectDelete(&m->sem);
353 delete m;
354}
355
356/*virtual*/ bool WriteLockHandle::isWriteLockOnCurrentThread() const
357{
358 return RTCritSectIsOwner(&m->sem);
359}
360
361/*virtual*/ void WriteLockHandle::lockWrite(LOCKVAL_SRC_POS_DECL)
362{
363#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
364 validateLock(LOCKVAL_SRC_POS_ARGS);
365#endif
366
367#if defined(RT_STRICT) && defined(VBOX_WITH_DEBUG_LOCK_VALIDATOR)
368 RTCritSectEnterDebug(&m->sem, (uintptr_t)ASMReturnAddress(), RT_SRC_POS_ARGS);
369#elif defined(RT_STRICT)
370 RTCritSectEnterDebug(&m->sem, (uintptr_t)ASMReturnAddress(),
371 "return address >>>", 0, __PRETTY_FUNCTION__);
372#else
373 RTCritSectEnter(&m->sem);
374#endif
375}
376
377/*virtual*/ void WriteLockHandle::unlockWrite()
378{
379#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
380 validateUnlock();
381#endif
382
383 RTCritSectLeave(&m->sem);
384}
385
386/*virtual*/ void WriteLockHandle::lockRead(LOCKVAL_SRC_POS_DECL)
387{
388 lockWrite(LOCKVAL_SRC_POS_ARGS);
389}
390
391/*virtual*/ void WriteLockHandle::unlockRead()
392{
393 unlockWrite();
394}
395
396/*virtual*/ uint32_t WriteLockHandle::writeLockLevel() const
397{
398 return RTCritSectGetRecursion(&m->sem);
399}
400
401#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
402/*virtual*/ const char* WriteLockHandle::describe() const
403{
404 return m->strDescription.c_str();
405}
406#endif
407
408////////////////////////////////////////////////////////////////////////////////
409//
410// AutoLockBase
411//
412////////////////////////////////////////////////////////////////////////////////
413
414typedef std::vector<LockHandle*> HandlesVector;
415typedef std::vector<uint32_t> CountsVector;
416
417struct AutoLockBase::Data
418{
419 Data(size_t cHandles
420#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
421 , const char *pcszFile_,
422 unsigned uLine_,
423 const char *pcszFunction_
424#endif
425 )
426 : fIsLocked(false),
427 aHandles(cHandles), // size of array
428 acUnlockedInLeave(cHandles)
429#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
430 , pcszFile(pcszFile_),
431 uLine(uLine_),
432 pcszFunction(pcszFunction_)
433#endif
434 {
435 for (uint32_t i = 0; i < cHandles; ++i)
436 {
437 acUnlockedInLeave[i] = 0;
438 aHandles[i] = NULL;
439 }
440 }
441
442 bool fIsLocked; // if true, then all items in aHandles are locked by this AutoLock and
443 // need to be unlocked in the destructor
444 HandlesVector aHandles; // array (vector) of LockHandle instances; in the case of AutoWriteLock
445 // and AutoReadLock, there will only be one item on the list; with the
446 // AutoMulti* derivatives, there will be multiple
447 CountsVector acUnlockedInLeave; // for each lock handle, how many times the handle was unlocked in leave(); otherwise 0
448
449#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
450 // information about where the lock occured (passed down from the AutoLock classes)
451 const char *pcszFile;
452 unsigned uLine;
453 const char *pcszFunction;
454#endif
455};
456
457AutoLockBase::AutoLockBase(uint32_t cHandles
458 COMMA_LOCKVAL_SRC_POS_DECL)
459{
460 m = new Data(cHandles
461 COMMA_LOCKVAL_SRC_POS_ARGS);
462}
463
464AutoLockBase::AutoLockBase(uint32_t cHandles,
465 LockHandle *pHandle
466 COMMA_LOCKVAL_SRC_POS_DECL)
467{
468 Assert(cHandles == 1);
469 m = new Data(1
470 COMMA_LOCKVAL_SRC_POS_ARGS);
471 m->aHandles[0] = pHandle;
472}
473
474AutoLockBase::~AutoLockBase()
475{
476 delete m;
477}
478
479/**
480 * Requests ownership of all contained lock handles by calling
481 * the pure virtual callLockImpl() function on each of them,
482 * which must be implemented by the descendant class; in the
483 * implementation, AutoWriteLock will request a write lock
484 * whereas AutoReadLock will request a read lock.
485 *
486 * Does *not* modify the lock counts in the member variables.
487 */
488void AutoLockBase::callLockOnAllHandles()
489{
490 for (HandlesVector::iterator it = m->aHandles.begin();
491 it != m->aHandles.end();
492 ++it)
493 {
494 LockHandle *pHandle = *it;
495 if (pHandle)
496 // call virtual function implemented in AutoWriteLock or AutoReadLock
497 this->callLockImpl(*pHandle);
498 }
499}
500
501/**
502 * Releases ownership of all contained lock handles by calling
503 * the pure virtual callUnlockImpl() function on each of them,
504 * which must be implemented by the descendant class; in the
505 * implementation, AutoWriteLock will release a write lock
506 * whereas AutoReadLock will release a read lock.
507 *
508 * Does *not* modify the lock counts in the member variables.
509 */
510void AutoLockBase::callUnlockOnAllHandles()
511{
512 // unlock in reverse order!
513 for (HandlesVector::reverse_iterator it = m->aHandles.rbegin();
514 it != m->aHandles.rend();
515 ++it)
516 {
517 LockHandle *pHandle = *it;
518 if (pHandle)
519 // call virtual function implemented in AutoWriteLock or AutoReadLock
520 this->callUnlockImpl(*pHandle);
521 }
522}
523
524/**
525 * Destructor implementation that can also be called explicitly, if required.
526 * Restores the exact state before the AutoLock was created; that is, unlocks
527 * all contained semaphores and might actually lock them again if leave()
528 * was called during the AutoLock's lifetime.
529 */
530void AutoLockBase::cleanup()
531{
532 bool fAnyUnlockedInLeave = false;
533
534 uint32_t i = 0;
535 for (HandlesVector::iterator it = m->aHandles.begin();
536 it != m->aHandles.end();
537 ++it)
538 {
539 LockHandle *pHandle = *it;
540 if (pHandle)
541 {
542 if (m->acUnlockedInLeave[i])
543 {
544 // there was a leave() before the destruction: then restore the
545 // lock level that might have been set by locks other than our own
546 if (m->fIsLocked)
547 {
548 --m->acUnlockedInLeave[i];
549 fAnyUnlockedInLeave = true;
550 }
551 for (; m->acUnlockedInLeave[i]; --m->acUnlockedInLeave[i])
552 callLockImpl(*pHandle);
553 }
554 }
555 ++i;
556 }
557
558 if (m->fIsLocked && !fAnyUnlockedInLeave)
559 callUnlockOnAllHandles();
560}
561
562/**
563 * Requests ownership of all contained semaphores. Public method that can
564 * only be called once and that also gets called by the AutoLock constructors.
565 */
566void AutoLockBase::acquire()
567{
568 AssertMsg(!m->fIsLocked, ("m->fIsLocked is true, attempting to lock twice!"));
569 callLockOnAllHandles();
570 m->fIsLocked = true;
571}
572
573/**
574 * Releases ownership of all contained semaphores. Public method.
575 */
576void AutoLockBase::release()
577{
578 AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot release!"));
579 callUnlockOnAllHandles();
580 m->fIsLocked = false;
581}
582
583#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
584void AutoLockBase::dumpStack(const char *pcszMessage, RT_SRC_POS_DECL)
585{
586 LockingStack *pStack = getThreadLocalLockingStack();
587 LogFlow(("LOCKVAL DUMPSTACK at %s (%s:%u)\nLOCKVAL DUMPSTACK %s\n", pszFunction, pszFile, iLine, pcszMessage));
588 dumpThreadLocalLockingStack(pStack);
589}
590#endif
591
592////////////////////////////////////////////////////////////////////////////////
593//
594// AutoReadLock
595//
596////////////////////////////////////////////////////////////////////////////////
597
598/**
599 * Release all read locks acquired by this instance through the #lock()
600 * call and destroys the instance.
601 *
602 * Note that if there there are nested #lock() calls without the
603 * corresponding number of #unlock() calls when the destructor is called, it
604 * will assert. This is because having an unbalanced number of nested locks
605 * is a program logic error which must be fixed.
606 */
607/*virtual*/ AutoReadLock::~AutoReadLock()
608{
609 LockHandle *pHandle = m->aHandles[0];
610
611 if (pHandle)
612 {
613 if (m->fIsLocked)
614 callUnlockImpl(*pHandle);
615 }
616}
617
618/**
619 * Implementation of the pure virtual declared in AutoLockBase.
620 * This gets called by AutoLockBase.acquire() to actually request
621 * the semaphore; in the AutoReadLock implementation, we request
622 * the semaphore in read mode.
623 */
624/*virtual*/ void AutoReadLock::callLockImpl(LockHandle &l)
625{
626#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
627 l.lockRead(m->pcszFile, m->uLine, m->pcszFunction);
628#else
629 l.lockRead();
630#endif
631}
632
633/**
634 * Implementation of the pure virtual declared in AutoLockBase.
635 * This gets called by AutoLockBase.release() to actually release
636 * the semaphore; in the AutoReadLock implementation, we release
637 * the semaphore in read mode.
638 */
639/*virtual*/ void AutoReadLock::callUnlockImpl(LockHandle &l)
640{
641 l.unlockRead();
642}
643
644////////////////////////////////////////////////////////////////////////////////
645//
646// AutoWriteLockBase
647//
648////////////////////////////////////////////////////////////////////////////////
649
650/**
651 * Implementation of the pure virtual declared in AutoLockBase.
652 * This gets called by AutoLockBase.acquire() to actually request
653 * the semaphore; in the AutoWriteLock implementation, we request
654 * the semaphore in write mode.
655 */
656/*virtual*/ void AutoWriteLockBase::callLockImpl(LockHandle &l)
657{
658#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
659 l.lockWrite(m->pcszFile, m->uLine, m->pcszFunction);
660#else
661 l.lockWrite();
662#endif
663}
664
665/**
666 * Implementation of the pure virtual declared in AutoLockBase.
667 * This gets called by AutoLockBase.release() to actually release
668 * the semaphore; in the AutoWriteLock implementation, we release
669 * the semaphore in write mode.
670 */
671/*virtual*/ void AutoWriteLockBase::callUnlockImpl(LockHandle &l)
672{
673 l.unlockWrite();
674}
675
676/**
677 * Causes the current thread to completely release the write lock to make
678 * the managed semaphore immediately available for locking by other threads.
679 *
680 * This implies that all nested write locks on the semaphore will be
681 * released, even those that were acquired through the calls to #lock()
682 * methods of all other AutoWriteLock/AutoReadLock instances managing the
683 * <b>same</b> read/write semaphore.
684 *
685 * After calling this method, the only method you are allowed to call is
686 * #enter(). It will acquire the write lock again and restore the same
687 * level of nesting as it had before calling #leave().
688 *
689 * If this instance is destroyed without calling #enter(), the destructor
690 * will try to restore the write lock level that existed when #leave() was
691 * called minus the number of nested #lock() calls made on this instance
692 * itself. This is done to preserve lock levels of other
693 * AutoWriteLock/AutoReadLock instances managing the same semaphore (if
694 * any). Tiis also means that the destructor may indefinitely block if a
695 * write or a read lock is owned by some other thread by that time.
696 */
697void AutoWriteLockBase::leave()
698{
699 AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot leave()!"));
700
701 // unlock in reverse order!
702 uint32_t i = m->aHandles.size();
703 for (HandlesVector::reverse_iterator it = m->aHandles.rbegin();
704 it != m->aHandles.rend();
705 ++it)
706 {
707 --i; // array index is zero based, decrement with every loop since we iterate backwards
708 LockHandle *pHandle = *it;
709 if (pHandle)
710 {
711 AssertMsg(m->acUnlockedInLeave[i] == 0, ("m->cUnlockedInLeave[%d] is %d, must be 0! Called leave() twice?", i, m->acUnlockedInLeave[i]));
712 m->acUnlockedInLeave[i] = pHandle->writeLockLevel();
713 AssertMsg(m->acUnlockedInLeave[i] >= 1, ("m->cUnlockedInLeave[%d] is %d, must be >=1!", i, m->acUnlockedInLeave[i]));
714
715#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
716 LogFlowFunc(("LOCKVAL: will unlock handle %d [%s] %d times\n", i, pHandle->describe(), m->acUnlockedInLeave[i]));
717#endif
718
719 for (uint32_t left = m->acUnlockedInLeave[i];
720 left;
721 --left)
722 callUnlockImpl(*pHandle);
723 }
724 }
725}
726
727/**
728 * Causes the current thread to restore the write lock level after the
729 * #leave() call. This call will indefinitely block if another thread has
730 * successfully acquired a write or a read lock on the same semaphore in
731 * between.
732 */
733void AutoWriteLockBase::enter()
734{
735 AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot enter()!"));
736
737 uint32_t i = 0;
738 for (HandlesVector::iterator it = m->aHandles.begin();
739 it != m->aHandles.end();
740 ++it)
741 {
742 LockHandle *pHandle = *it;
743 if (pHandle)
744 {
745 AssertMsg(m->acUnlockedInLeave[i] != 0, ("m->cUnlockedInLeave[%d] is 0! enter() without leave()?", i));
746
747#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
748 LogFlowFunc(("LOCKVAL: will lock handle %d [%s] %d times\n", i, pHandle->describe(), m->acUnlockedInLeave[i]));
749#endif
750
751 for (; m->acUnlockedInLeave[i]; --m->acUnlockedInLeave[i])
752 callLockImpl(*pHandle);
753 }
754 ++i;
755 }
756}
757
758/**
759 * Same as #leave() but checks if the current thread actally owns the lock
760 * and only proceeds in this case. As a result, as opposed to #leave(),
761 * doesn't assert when called with no lock being held.
762 */
763void AutoWriteLockBase::maybeLeave()
764{
765 // unlock in reverse order!
766 uint32_t i = m->aHandles.size();
767 for (HandlesVector::reverse_iterator it = m->aHandles.rbegin();
768 it != m->aHandles.rend();
769 ++it)
770 {
771 --i; // array index is zero based, decrement with every loop since we iterate backwards
772 LockHandle *pHandle = *it;
773 if (pHandle)
774 {
775 if (pHandle->isWriteLockOnCurrentThread())
776 {
777 m->acUnlockedInLeave[i] = pHandle->writeLockLevel();
778 AssertMsg(m->acUnlockedInLeave[i] >= 1, ("m->cUnlockedInLeave[%d] is %d, must be >=1!", i, m->acUnlockedInLeave[i]));
779
780#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
781 LogFlowFunc(("LOCKVAL: will unlock handle %d [%s] %d times\n", i, pHandle->describe(), m->acUnlockedInLeave[i]));
782#endif
783
784 for (uint32_t left = m->acUnlockedInLeave[i];
785 left;
786 --left)
787 callUnlockImpl(*pHandle);
788 }
789 }
790 ++i;
791 }
792}
793
794/**
795 * Same as #enter() but checks if the current thread actally owns the lock
796 * and only proceeds if not. As a result, as opposed to #enter(), doesn't
797 * assert when called with the lock already being held.
798 */
799void AutoWriteLockBase::maybeEnter()
800{
801 uint32_t i = 0;
802 for (HandlesVector::iterator it = m->aHandles.begin();
803 it != m->aHandles.end();
804 ++it)
805 {
806 LockHandle *pHandle = *it;
807 if (pHandle)
808 {
809 if (!pHandle->isWriteLockOnCurrentThread())
810 {
811#ifdef VBOX_WITH_DEBUG_LOCK_VALIDATOR
812 LogFlowFunc(("LOCKVAL: will lock handle %d [%s] %d times\n", i, pHandle->describe(), m->acUnlockedInLeave[i]));
813#endif
814
815 for (; m->acUnlockedInLeave[i]; --m->acUnlockedInLeave[i])
816 callLockImpl(*pHandle);
817 }
818 }
819 ++i;
820 }
821}
822
823////////////////////////////////////////////////////////////////////////////////
824//
825// AutoWriteLock
826//
827////////////////////////////////////////////////////////////////////////////////
828
829/**
830 * Attaches another handle to this auto lock instance.
831 *
832 * The previous object's lock is completely released before the new one is
833 * acquired. The lock level of the new handle will be the same. This
834 * also means that if the lock was not acquired at all before #attach(), it
835 * will not be acquired on the new handle too.
836 *
837 * @param aHandle New handle to attach.
838 */
839void AutoWriteLock::attach(LockHandle *aHandle)
840{
841 LockHandle *pHandle = m->aHandles[0];
842
843 /* detect simple self-reattachment */
844 if (pHandle != aHandle)
845 {
846 bool fWasLocked = m->fIsLocked;
847
848 cleanup();
849
850 m->aHandles[0] = aHandle;
851 m->fIsLocked = fWasLocked;
852
853 if (aHandle)
854 if (fWasLocked)
855 callLockImpl(*aHandle);
856 }
857}
858
859/**
860 * Returns @c true if the current thread holds a write lock on the managed
861 * read/write semaphore. Returns @c false if the managed semaphore is @c
862 * NULL.
863 *
864 * @note Intended for debugging only.
865 */
866bool AutoWriteLock::isWriteLockOnCurrentThread() const
867{
868 return m->aHandles[0] ? m->aHandles[0]->isWriteLockOnCurrentThread() : false;
869}
870
871 /**
872 * Returns the current write lock level of the managed smaphore. The lock
873 * level determines the number of nested #lock() calls on the given
874 * semaphore handle. Returns @c 0 if the managed semaphore is @c
875 * NULL.
876 *
877 * Note that this call is valid only when the current thread owns a write
878 * lock on the given semaphore handle and will assert otherwise.
879 *
880 * @note Intended for debugging only.
881 */
882uint32_t AutoWriteLock::writeLockLevel() const
883{
884 return m->aHandles[0] ? m->aHandles[0]->writeLockLevel() : 0;
885}
886
887////////////////////////////////////////////////////////////////////////////////
888//
889// AutoMultiWriteLock*
890//
891////////////////////////////////////////////////////////////////////////////////
892
893AutoMultiWriteLock2::AutoMultiWriteLock2(Lockable *pl1,
894 Lockable *pl2
895 COMMA_LOCKVAL_SRC_POS_DECL)
896 : AutoWriteLockBase(2
897 COMMA_LOCKVAL_SRC_POS_ARGS)
898{
899 if (pl1)
900 m->aHandles[0] = pl1->lockHandle();
901 if (pl2)
902 m->aHandles[1] = pl2->lockHandle();
903 acquire();
904}
905
906AutoMultiWriteLock2::AutoMultiWriteLock2(LockHandle *pl1,
907 LockHandle *pl2
908 COMMA_LOCKVAL_SRC_POS_DECL)
909 : AutoWriteLockBase(2
910 COMMA_LOCKVAL_SRC_POS_ARGS)
911{
912 m->aHandles[0] = pl1;
913 m->aHandles[1] = pl2;
914 acquire();
915}
916
917AutoMultiWriteLock3::AutoMultiWriteLock3(Lockable *pl1,
918 Lockable *pl2,
919 Lockable *pl3
920 COMMA_LOCKVAL_SRC_POS_DECL)
921 : AutoWriteLockBase(3
922 COMMA_LOCKVAL_SRC_POS_ARGS)
923{
924 if (pl1)
925 m->aHandles[0] = pl1->lockHandle();
926 if (pl2)
927 m->aHandles[1] = pl2->lockHandle();
928 if (pl3)
929 m->aHandles[2] = pl3->lockHandle();
930 acquire();
931}
932
933AutoMultiWriteLock3::AutoMultiWriteLock3(LockHandle *pl1,
934 LockHandle *pl2,
935 LockHandle *pl3
936 COMMA_LOCKVAL_SRC_POS_DECL)
937 : AutoWriteLockBase(3
938 COMMA_LOCKVAL_SRC_POS_ARGS)
939{
940 m->aHandles[0] = pl1;
941 m->aHandles[1] = pl2;
942 m->aHandles[2] = pl3;
943 acquire();
944}
945
946} /* namespace util */
947/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use