VirtualBox

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

Last change on this file since 25368 was 25368, checked in by vboxsync, 15 years ago

RTCritSect,PDMCritSect,iprt/lockvalidator.h: Reworked the deadlocking detection for critical sections and preparing for lock order validation. This change generalizes the RTCRITSECT::Strict data and moves it out of the RTCRITSECT, leaving a pointer behind. This saves a bit of space in release builds.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette