VirtualBox

source: vbox/trunk/src/VBox/Main/include/AutoLock.h@ 25279

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

Main: preparation for deadlock detection: clean up autolock classes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.8 KB
Line 
1/** @file
2 *
3 * AutoWriteLock/AutoReadLock: smart R/W semaphore wrappers
4 */
5
6/*
7 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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#ifndef ____H_AUTOLOCK
23#define ____H_AUTOLOCK
24
25#include <iprt/cdefs.h>
26#include <iprt/types.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/semaphore.h>
39
40namespace util
41{
42
43////////////////////////////////////////////////////////////////////////////////
44//
45// LockHandle and friends
46//
47////////////////////////////////////////////////////////////////////////////////
48
49/**
50 * Abstract lock operations. See LockHandle and AutoWriteLock for details.
51 */
52class LockOps
53{
54public:
55
56 virtual ~LockOps() {}
57
58 virtual void lock() = 0;
59 virtual void unlock() = 0;
60};
61
62/**
63 * Read lock operations. See LockHandle and AutoWriteLock for details.
64 */
65class ReadLockOps : public LockOps
66{
67public:
68
69 /**
70 * Requests a read (shared) lock.
71 */
72 virtual void lockRead() = 0;
73
74 /**
75 * Releases a read (shared) lock ackquired by lockRead().
76 */
77 virtual void unlockRead() = 0;
78
79 // LockOps interface
80 void lock() { lockRead(); }
81 void unlock() { unlockRead(); }
82};
83
84/**
85 * Write lock operations. See LockHandle and AutoWriteLock for details.
86 */
87class WriteLockOps : public LockOps
88{
89public:
90
91 /**
92 * Requests a write (exclusive) lock.
93 */
94 virtual void lockWrite() = 0;
95
96 /**
97 * Releases a write (exclusive) lock ackquired by lockWrite().
98 */
99 virtual void unlockWrite() = 0;
100
101 // LockOps interface
102 void lock() { lockWrite(); }
103 void unlock() { unlockWrite(); }
104};
105
106/**
107 * Abstract read/write semaphore handle.
108 *
109 * This is a base class to implement semaphores that provide read/write locking.
110 * Subclasses must implement all pure virtual methods of this class together
111 * with pure methods of ReadLockOps and WriteLockOps classes.
112 *
113 * See the AutoWriteLock class documentation for the detailed description of
114 * read and write locks.
115 */
116class LockHandle : protected ReadLockOps, protected WriteLockOps
117{
118public:
119
120 LockHandle() {}
121 virtual ~LockHandle() {}
122
123 /**
124 * Returns @c true if the current thread holds a write lock on this
125 * read/write semaphore. Intended for debugging only.
126 */
127 virtual bool isWriteLockOnCurrentThread() const = 0;
128
129 /**
130 * Returns the current write lock level of this semaphore. The lock level
131 * determines the number of nested #lock() calls on the given semaphore
132 * handle.
133 *
134 * Note that this call is valid only when the current thread owns a write
135 * lock on the given semaphore handle and will assert otherwise.
136 */
137 virtual uint32_t writeLockLevel() const = 0;
138
139 /**
140 * Returns an interface to read lock operations of this semaphore.
141 * Used by constructors of AutoMultiLockN classes.
142 */
143 LockOps *rlock() { return (ReadLockOps *) this; }
144
145 /**
146 * Returns an interface to write lock operations of this semaphore.
147 * Used by constructors of AutoMultiLockN classes.
148 */
149 LockOps *wlock() { return (WriteLockOps *) this; }
150
151private:
152
153 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (LockHandle)
154
155 friend class AutoWriteLock;
156 friend class AutoReadLock;
157};
158
159/**
160 * Full-featured read/write semaphore handle implementation.
161 *
162 * This is an auxiliary base class for classes that need full-featured
163 * read/write locking as described in the AutoWriteLock class documentation.
164 * Instances of classes inherited from this class can be passed as arguments to
165 * the AutoWriteLock and AutoReadLock constructors.
166 */
167class RWLockHandle : public LockHandle
168{
169public:
170
171 RWLockHandle();
172 virtual ~RWLockHandle();
173
174 bool isWriteLockOnCurrentThread() const;
175
176private:
177
178 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (RWLockHandle)
179
180 void lockWrite();
181 void unlockWrite();
182 void lockRead();
183 void unlockRead();
184
185 uint32_t writeLockLevel() const;
186
187 RTSEMRW mSemRW;
188};
189
190/**
191 * Write-only semaphore handle implementation.
192 *
193 * This is an auxiliary base class for classes that need write-only (exclusive)
194 * locking and do not need read (shared) locking. This implementation uses a
195 * cheap and fast critical section for both lockWrite() and lockRead() methods
196 * which makes a lockRead() call fully equivalent to the lockWrite() call and
197 * therefore makes it pointless to use instahces of this class with
198 * AutoReadLock instances -- shared locking will not be possible anyway and
199 * any call to lock() will block if there are lock owners on other threads.
200 *
201 * Use with care only when absolutely sure that shared locks are not necessary.
202 */
203class WriteLockHandle : public LockHandle
204{
205public:
206
207 WriteLockHandle()
208 {
209 RTCritSectInit (&mCritSect);
210 }
211
212 virtual ~WriteLockHandle()
213 {
214 RTCritSectDelete (&mCritSect);
215 }
216
217 bool isWriteLockOnCurrentThread() const
218 {
219 return RTCritSectIsOwner (&mCritSect);
220 }
221
222private:
223
224 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (WriteLockHandle)
225
226 void lockWrite()
227 {
228#if defined(DEBUG)
229 RTCritSectEnterDebug (&mCritSect,
230 "WriteLockHandle::lockWrite() return address >>>",
231 0, (RTUINTPTR) ASMReturnAddress());
232#else
233 RTCritSectEnter (&mCritSect);
234#endif
235 }
236
237 void unlockWrite()
238 {
239 RTCritSectLeave (&mCritSect);
240 }
241
242 void lockRead() { lockWrite(); }
243 void unlockRead() { unlockWrite(); }
244
245 uint32_t writeLockLevel() const
246 {
247 return RTCritSectGetRecursion (&mCritSect);
248 }
249
250 mutable RTCRITSECT mCritSect;
251};
252
253////////////////////////////////////////////////////////////////////////////////
254//
255// Lockable
256//
257////////////////////////////////////////////////////////////////////////////////
258
259/**
260 * Lockable interface.
261 *
262 * This is an abstract base for classes that need read/write locking. Unlike
263 * RWLockHandle and other classes that makes the read/write semaphore a part of
264 * class data, this class allows subclasses to decide which semaphore handle to
265 * use.
266 */
267class Lockable
268{
269public:
270
271 /**
272 * Returns a pointer to a LockHandle used by AutoWriteLock/AutoReadLock
273 * for locking. Subclasses are allowed to return @c NULL -- in this case,
274 * the AutoWriteLock/AutoReadLock object constructed using an instance of
275 * such subclass will simply turn into no-op.
276 */
277 virtual LockHandle *lockHandle() const = 0;
278
279 /**
280 * Equivalent to <tt>#lockHandle()->isWriteLockOnCurrentThread()</tt>.
281 * Returns @c false if lockHandle() returns @c NULL.
282 */
283 bool isWriteLockOnCurrentThread()
284 {
285 LockHandle *h = lockHandle();
286 return h ? h->isWriteLockOnCurrentThread() : false;
287 }
288
289 /**
290 * Equivalent to <tt>#lockHandle()->rlock()</tt>.
291 * Returns @c NULL false if lockHandle() returns @c NULL.
292 */
293 LockOps *rlock()
294 {
295 LockHandle *h = lockHandle();
296 return h ? h->rlock() : NULL;
297 }
298
299 /**
300 * Equivalent to <tt>#lockHandle()->wlock()</tt>. Returns @c NULL false if
301 * lockHandle() returns @c NULL.
302 */
303 LockOps *wlock()
304 {
305 LockHandle *h = lockHandle();
306 return h ? h->wlock() : NULL;
307 }
308};
309
310////////////////////////////////////////////////////////////////////////////////
311//
312// AutoLockBase
313//
314////////////////////////////////////////////////////////////////////////////////
315
316class AutoLockBase
317{
318protected:
319 AutoLockBase(LockHandle *pHandle)
320 : mHandle(pHandle),
321 mLockLevel(0),
322 mGlobalLockLevel(0)
323 { }
324
325 LockHandle *mHandle;
326 uint32_t mLockLevel;
327 uint32_t mGlobalLockLevel;
328};
329
330////////////////////////////////////////////////////////////////////////////////
331//
332// AutoWriteLock
333//
334////////////////////////////////////////////////////////////////////////////////
335
336class AutoWriteLock : public AutoLockBase
337{
338public:
339
340 /**
341 * Constructs a null instance that does not manage any read/write
342 * semaphore.
343 *
344 * Note that all method calls on a null instance are no-ops. This allows to
345 * have the code where lock protection can be selected (or omitted) at
346 * runtime.
347 */
348 AutoWriteLock()
349 : AutoLockBase(NULL)
350 { }
351
352 /**
353 * Constructs a new instance that will start managing the given read/write
354 * semaphore by requesting a write lock.
355 */
356 AutoWriteLock(LockHandle *aHandle)
357 : AutoLockBase(aHandle)
358 {
359 acquire();
360 }
361
362 /**
363 * Constructs a new instance that will start managing the given read/write
364 * semaphore by requesting a write lock.
365 */
366 AutoWriteLock(LockHandle &aHandle)
367 : AutoLockBase(&aHandle)
368 {
369 acquire();
370 }
371
372 /**
373 * Constructs a new instance that will start managing the given read/write
374 * semaphore by requesting a write lock.
375 */
376 AutoWriteLock(const Lockable &aLockable)
377 : AutoLockBase(aLockable.lockHandle())
378 {
379 acquire();
380 }
381
382 /**
383 * Constructs a new instance that will start managing the given read/write
384 * semaphore by requesting a write lock.
385 */
386 AutoWriteLock(const Lockable *aLockable)
387 : AutoLockBase(aLockable ? aLockable->lockHandle() : NULL)
388 {
389 acquire();
390 }
391
392 /**
393 * Release all write locks acquired by this instance through the #lock()
394 * call and destroys the instance.
395 *
396 * Note that if there there are nested #lock() calls without the
397 * corresponding number of #unlock() calls when the destructor is called, it
398 * will assert. This is because having an unbalanced number of nested locks
399 * is a program logic error which must be fixed.
400 */
401 ~AutoWriteLock()
402 {
403 if (mHandle)
404 {
405 if (mGlobalLockLevel)
406 {
407 mGlobalLockLevel -= mLockLevel;
408 mLockLevel = 0;
409 for (; mGlobalLockLevel; --mGlobalLockLevel)
410 mHandle->lockWrite();
411 }
412
413 AssertMsg(mLockLevel <= 1, ("Lock level > 1: %d\n", mLockLevel));
414 for (; mLockLevel; --mLockLevel)
415 mHandle->unlockWrite();
416 }
417 }
418
419 /**
420 * Requests a write (exclusive) lock. If a write lock is already owned by
421 * this thread, increases the lock level (allowing for nested write locks on
422 * the same thread). Blocks indefinitely if a write lock or a read lock is
423 * already owned by another thread until that tread releases the locks,
424 * otherwise returns immediately.
425 */
426 void acquire()
427 {
428 if (mHandle)
429 {
430 mHandle->lockWrite();
431 ++mLockLevel;
432 Assert(mLockLevel != 0 /* overflow? */);
433 }
434 }
435
436 /**
437 * Decreases the write lock level increased by #lock(). If the level drops
438 * to zero (e.g. the number of nested #unlock() calls matches the number of
439 * nested #lock() calls), releases the lock making the managed semaphore
440 * available for locking by other threads.
441 */
442 void release()
443 {
444 if (mHandle)
445 {
446 AssertReturnVoid(mLockLevel != 0 /* unlock() w/o preceding lock()? */);
447 mHandle->unlockWrite();
448 --mLockLevel;
449 }
450 }
451
452 /**
453 * Causes the current thread to completely release the write lock to make
454 * the managed semaphore immediately available for locking by other threads.
455 *
456 * This implies that all nested write locks on the semaphore will be
457 * released, even those that were acquired through the calls to #lock()
458 * methods of all other AutoWriteLock/AutoReadLock instances managing the
459 * <b>same</b> read/write semaphore.
460 *
461 * After calling this method, the only method you are allowed to call is
462 * #enter(). It will acquire the write lock again and restore the same
463 * level of nesting as it had before calling #leave().
464 *
465 * If this instance is destroyed without calling #enter(), the destructor
466 * will try to restore the write lock level that existed when #leave() was
467 * called minus the number of nested #lock() calls made on this instance
468 * itself. This is done to preserve lock levels of other
469 * AutoWriteLock/AutoReadLock instances managing the same semaphore (if
470 * any). Tiis also means that the destructor may indefinitely block if a
471 * write or a read lock is owned by some other thread by that time.
472 */
473 void leave()
474 {
475 if (mHandle)
476 {
477 AssertReturnVoid(mLockLevel != 0 /* leave() w/o preceding lock()? */);
478 AssertReturnVoid(mGlobalLockLevel == 0 /* second leave() in a row? */);
479
480 mGlobalLockLevel = mHandle->writeLockLevel();
481 AssertReturnVoid(mGlobalLockLevel >= mLockLevel /* logic error! */);
482
483 for (uint32_t left = mGlobalLockLevel; left; --left)
484 mHandle->unlockWrite();
485 }
486 }
487
488 /**
489 * Same as #leave() but checks if the current thread actally owns the lock
490 * and only proceeds in this case. As a result, as opposed to #leave(),
491 * doesn't assert when called with no lock being held.
492 */
493 void maybeLeave()
494 {
495 if (isWriteLockOnCurrentThread())
496 leave();
497 }
498
499 /**
500 * Same as #enter() but checks if the current thread actally owns the lock
501 * and only proceeds if not. As a result, as opposed to #enter(), doesn't
502 * assert when called with the lock already being held.
503 */
504 void maybeEnter()
505 {
506 if (!isWriteLockOnCurrentThread())
507 enter();
508 }
509
510 /**
511 * Causes the current thread to restore the write lock level after the
512 * #leave() call. This call will indefinitely block if another thread has
513 * successfully acquired a write or a read lock on the same semaphore in
514 * between.
515 */
516 void enter()
517 {
518 if (mHandle)
519 {
520 AssertReturnVoid(mLockLevel != 0 /* enter() w/o preceding lock()+leave()? */);
521 AssertReturnVoid(mGlobalLockLevel != 0 /* enter() w/o preceding leave()? */);
522
523 for (; mGlobalLockLevel; --mGlobalLockLevel)
524 mHandle->lockWrite();
525 }
526 }
527
528 /**
529 * Attaches another handle to this auto lock instance.
530 *
531 * The previous object's lock is completely released before the new one is
532 * acquired. The lock level of the new handle will be the same. This
533 * also means that if the lock was not acquired at all before #attach(), it
534 * will not be acquired on the new handle too.
535 *
536 * @param aHandle New handle to attach.
537 */
538 void attach(LockHandle *aHandle)
539 {
540 /* detect simple self-reattachment */
541 if (mHandle != aHandle)
542 {
543 uint32_t lockLevel = mLockLevel;
544
545 /* perform the destructor part */
546 if (mHandle)
547 {
548 if (mGlobalLockLevel)
549 {
550 mGlobalLockLevel -= mLockLevel;
551 mLockLevel = 0;
552 for (; mGlobalLockLevel; --mGlobalLockLevel)
553 mHandle->lockWrite();
554 }
555
556 AssertMsg(mLockLevel <= 1, ("Lock level > 1: %d\n", mLockLevel));
557 for (; mLockLevel; --mLockLevel)
558 mHandle->unlockWrite();
559 }
560
561 mHandle = aHandle;
562 mLockLevel = lockLevel;
563
564 if (mHandle)
565 for (; lockLevel; --lockLevel)
566 mHandle->lockWrite();
567 }
568 }
569
570 /** @see attach (LockHandle *) */
571 void attach(LockHandle &aHandle)
572 {
573 attach(&aHandle);
574 }
575
576 /** @see attach (LockHandle *) */
577 void attach(const Lockable &aLockable)
578 {
579 attach(aLockable.lockHandle());
580 }
581
582 /** @see attach (LockHandle *) */
583 void attach(const Lockable *aLockable)
584 {
585 attach(aLockable ? aLockable->lockHandle() : NULL);
586 }
587
588 /**
589 * Returns @c true if the current thread holds a write lock on the managed
590 * read/write semaphore. Returns @c false if the managed semaphore is @c
591 * NULL.
592 *
593 * @note Intended for debugging only.
594 */
595 bool isWriteLockOnCurrentThread() const
596 {
597 return mHandle ? mHandle->isWriteLockOnCurrentThread() : false;
598 }
599
600 /**
601 * Returns the current write lock level of the managed smaphore. The lock
602 * level determines the number of nested #lock() calls on the given
603 * semaphore handle. Returns @c 0 if the managed semaphore is @c
604 * NULL.
605 *
606 * Note that this call is valid only when the current thread owns a write
607 * lock on the given semaphore handle and will assert otherwise.
608 *
609 * @note Intended for debugging only.
610 */
611 uint32_t writeLockLevel() const
612 {
613 return mHandle ? mHandle->writeLockLevel() : 0;
614 }
615
616private:
617
618 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoWriteLock)
619 DECLARE_CLS_NEW_DELETE_NOOP (AutoWriteLock)
620
621 template <size_t> friend class AutoMultiWriteLockBase;
622};
623
624////////////////////////////////////////////////////////////////////////////////
625//
626// AutoReadLock
627//
628////////////////////////////////////////////////////////////////////////////////
629
630class AutoReadLock : public AutoLockBase
631{
632public:
633
634 /**
635 * Constructs a null instance that does not manage any read/write
636 * semaphore.
637 *
638 * Note that all method calls on a null instance are no-ops. This allows to
639 * have the code where lock protection can be selected (or omitted) at
640 * runtime.
641 */
642 AutoReadLock()
643 : AutoLockBase(NULL)
644 { }
645
646 /**
647 * Constructs a new instance that will start managing the given read/write
648 * semaphore by requesting a read lock.
649 */
650 AutoReadLock(LockHandle *aHandle)
651 : AutoLockBase(aHandle)
652 {
653 acquire();
654 }
655
656 /**
657 * Constructs a new instance that will start managing the given read/write
658 * semaphore by requesting a read lock.
659 */
660 AutoReadLock(LockHandle &aHandle)
661 : AutoLockBase(&aHandle)
662 {
663 acquire();
664 }
665
666 /**
667 * Constructs a new instance that will start managing the given read/write
668 * semaphore by requesting a read lock.
669 */
670 AutoReadLock(const Lockable &aLockable)
671 : AutoLockBase(aLockable.lockHandle())
672 {
673 acquire();
674 }
675
676 /**
677 * Constructs a new instance that will start managing the given read/write
678 * semaphore by requesting a read lock.
679 */
680 AutoReadLock(const Lockable *aLockable)
681 : AutoLockBase(aLockable ? aLockable->lockHandle() : NULL)
682 {
683 acquire();
684 }
685
686 /**
687 * Release all read locks acquired by this instance through the #lock()
688 * call and destroys the instance.
689 *
690 * Note that if there there are nested #lock() calls without the
691 * corresponding number of #unlock() calls when the destructor is called, it
692 * will assert. This is because having an unbalanced number of nested locks
693 * is a program logic error which must be fixed.
694 */
695 ~AutoReadLock()
696 {
697 if (mHandle)
698 {
699 AssertMsg(mLockLevel <= 1, ("Lock level > 1: %d\n", mLockLevel));
700 for (; mLockLevel; --mLockLevel)
701 mHandle->unlockRead();
702 }
703 }
704
705 /**
706 * Requests a read (shared) lock. If a read lock is already owned by
707 * this thread, increases the lock level (allowing for nested read locks on
708 * the same thread). Blocks indefinitely if a write lock is already owned by
709 * another thread until that tread releases the write lock, otherwise
710 * returns immediately.
711 *
712 * Note that this method returns immediately even if any number of other
713 * threads owns read locks on the same semaphore. Also returns immediately
714 * if a write lock on this semaphore is owned by the current thread which
715 * allows for read locks nested into write locks on the same thread.
716 */
717 void acquire()
718 {
719 if (mHandle)
720 {
721 mHandle->lockRead();
722 ++mLockLevel;
723 Assert(mLockLevel != 0 /* overflow? */);
724 }
725 }
726
727 /**
728 * Decreases the read lock level increased by #lock(). If the level drops to
729 * zero (e.g. the number of nested #unlock() calls matches the number of
730 * nested #lock() calls), releases the lock making the managed semaphore
731 * available for locking by other threads.
732 */
733 void release()
734 {
735 if (mHandle)
736 {
737 AssertReturnVoid(mLockLevel != 0 /* unlock() w/o preceding lock()? */);
738 mHandle->unlockRead();
739 --mLockLevel;
740 }
741 }
742
743 /**
744 * Attaches another handle to this auto lock instance.
745 *
746 * The previous object's lock is completely released before the new one is
747 * acquired. The lock level of the new handle will be the same. This also
748 * means that if the lock was not acquired at all before #attach(), it will
749 * not be acquired on the new handle too.
750 *
751 * @param aHandle New handle to attach.
752 */
753 void attach(LockHandle *aHandle)
754 {
755 /* detect simple self-reattachment */
756 if (mHandle != aHandle)
757 {
758 uint32_t lockLevel = mLockLevel;
759 if (mHandle)
760 for (; mLockLevel; --mLockLevel)
761 mHandle->unlockRead();
762 mHandle = aHandle;
763 mLockLevel = lockLevel;
764 if (mHandle)
765 for (; lockLevel; --lockLevel)
766 mHandle->lockRead();
767 }
768 }
769
770 /** @see attach (LockHandle *) */
771 void attach(LockHandle &aHandle)
772 {
773 attach(&aHandle);
774 }
775
776 /** @see attach (LockHandle *) */
777 void attach(const Lockable &aLockable)
778 {
779 attach(aLockable.lockHandle());
780 }
781
782 /** @see attach (LockHandle *) */
783 void attach(const Lockable *aLockable)
784 {
785 attach(aLockable ? aLockable->lockHandle() : NULL);
786 }
787
788private:
789
790 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoReadLock)
791 DECLARE_CLS_NEW_DELETE_NOOP (AutoReadLock)
792};
793
794////////////////////////////////////////////////////////////////////////////////
795//
796// AutoMulti*
797//
798////////////////////////////////////////////////////////////////////////////////
799
800/**
801 * Helper template class for AutoMultiLockN classes.
802 *
803 * @param Cnt number of read/write semaphores to manage.
804 */
805template <size_t Cnt>
806class AutoMultiLockBase
807{
808public:
809
810 /**
811 * Releases all locks if not yet released by #unlock() and destroys the
812 * instance.
813 */
814 ~AutoMultiLockBase()
815 {
816 if (mIsLocked)
817 unlock();
818 }
819
820 /**
821 * Calls LockOps::lock() methods of all managed semaphore handles
822 * in order they were passed to the constructor.
823 *
824 * Note that as opposed to LockHandle::lock(), this call cannot be nested
825 * and will assert if so.
826 */
827 void lock()
828 {
829 AssertReturnVoid (!mIsLocked);
830
831 size_t i = 0;
832 while (i < RT_ELEMENTS (mOps))
833 if (mOps [i])
834 mOps [i ++]->lock();
835 mIsLocked = true;
836 }
837
838 /**
839 * Calls LockOps::unlock() methods of all managed semaphore handles in
840 * reverse to the order they were passed to the constructor.
841 *
842 * Note that as opposed to LockHandle::unlock(), this call cannot be nested
843 * and will assert if so.
844 */
845 void unlock()
846 {
847 AssertReturnVoid (mIsLocked);
848
849 AssertReturnVoid (RT_ELEMENTS (mOps) > 0);
850 size_t i = RT_ELEMENTS (mOps);
851 do
852 if (mOps [-- i])
853 mOps [i]->unlock();
854 while (i != 0);
855 mIsLocked = false;
856 }
857
858protected:
859
860 AutoMultiLockBase() : mIsLocked (false) {}
861
862 LockOps *mOps [Cnt];
863 bool mIsLocked;
864
865private:
866
867 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoMultiLockBase)
868 DECLARE_CLS_NEW_DELETE_NOOP (AutoMultiLockBase)
869};
870
871/** AutoMultiLockBase <0> is meaningless and forbidden. */
872template<>
873class AutoMultiLockBase <0> { private : AutoMultiLockBase(); };
874
875/** AutoMultiLockBase <1> is meaningless and forbidden. */
876template<>
877class AutoMultiLockBase <1> { private : AutoMultiLockBase(); };
878
879////////////////////////////////////////////////////////////////////////////////
880
881/* AutoMultiLockN class definitions */
882
883#define A(n) LockOps *l##n
884#define B(n) mOps [n] = l##n
885
886/**
887 * AutoMultiLock for 2 locks.
888 *
889 * The AutoMultiLockN family of classes provides a possibility to manage several
890 * read/write semaphores at once. This is handy if all managed semaphores need
891 * to be locked and unlocked synchronously and will also help to avoid locking
892 * order errors.
893 *
894 * Instances of AutoMultiLockN classes are constructed from a list of LockOps
895 * arguments. The AutoMultiLockBase::lock() method will make sure that the given
896 * list of semaphores represented by LockOps pointers will be locked in order
897 * they are passed to the constructor. The AutoMultiLockBase::unlock() method
898 * will make sure that they will be unlocked in reverse order.
899 *
900 * The type of the lock to request is specified for each semaphore individually
901 * using the corresponding LockOps getter of a LockHandle or Lockable object:
902 * LockHandle::wlock() in order to request a write lock or LockHandle::rlock()
903 * in order to request a read lock.
904 *
905 * Here is a typical usage pattern:
906 * <code>
907 * ...
908 * LockHandle data1, data2;
909 * ...
910 * {
911 * AutoMultiLock2 multiLock (data1.wlock(), data2.rlock());
912 * // both locks are held here:
913 * // - data1 is locked in write mode (like AutoWriteLock)
914 * // - data2 is locked in read mode (like AutoReadLock)
915 * }
916 * // both locks are released here
917 * </code>
918 */
919class AutoMultiLock2 : public AutoMultiLockBase <2>
920{
921public:
922 AutoMultiLock2 (A(0), A(1))
923 { B(0); B(1); lock(); }
924};
925
926/** AutoMultiLock for 3 locks. See AutoMultiLock2 for more information. */
927class AutoMultiLock3 : public AutoMultiLockBase <3>
928{
929public:
930 AutoMultiLock3 (A(0), A(1), A(2))
931 { B(0); B(1); B(2); lock(); }
932};
933
934/** AutoMultiLock for 4 locks. See AutoMultiLock2 for more information. */
935class AutoMultiLock4 : public AutoMultiLockBase <4>
936{
937public:
938 AutoMultiLock4 (A(0), A(1), A(2), A(3))
939 { B(0); B(1); B(2); B(3); lock(); }
940};
941
942#undef B
943#undef A
944
945////////////////////////////////////////////////////////////////////////////////
946
947/**
948 * Helper template class for AutoMultiWriteLockN classes.
949 *
950 * @param Cnt number of write semaphores to manage.
951 */
952template <size_t Cnt>
953class AutoMultiWriteLockBase
954{
955
956public:
957 /**
958 * Calls AutoWriteLock::acquire() methods for all managed semaphore handles in
959 * order they were passed to the constructor.
960 */
961 void acquire()
962 {
963 size_t i = 0;
964 while (i < RT_ELEMENTS(mLocks))
965 mLocks[i++].acquire();
966 }
967
968 /**
969 * Calls AutoWriteLock::unlock() methods for all managed semaphore handles
970 * in reverse to the order they were passed to the constructor.
971 */
972 void release()
973 {
974 AssertReturnVoid(RT_ELEMENTS(mLocks) > 0);
975 size_t i = RT_ELEMENTS(mLocks);
976 do
977 mLocks[--i].release();
978 while (i != 0);
979 }
980
981 /**
982 * Calls AutoWriteLock::leave() methods for all managed semaphore handles in
983 * reverse to the order they were passed to the constructor.
984 */
985 void leave()
986 {
987 AssertReturnVoid(RT_ELEMENTS(mLocks) > 0);
988 size_t i = RT_ELEMENTS(mLocks);
989 do
990 mLocks[--i].leave();
991 while (i != 0);
992 }
993
994 /**
995 * Calls AutoWriteLock::maybeLeave() methods for all managed semaphore
996 * handles in reverse to the order they were passed to the constructor.
997 */
998 void maybeLeave()
999 {
1000 AssertReturnVoid(RT_ELEMENTS(mLocks) > 0);
1001 size_t i = RT_ELEMENTS(mLocks);
1002 do
1003 mLocks [-- i].maybeLeave();
1004 while (i != 0);
1005 }
1006
1007 /**
1008 * Calls AutoWriteLock::maybeEnter() methods for all managed semaphore
1009 * handles in order they were passed to the constructor.
1010 */
1011 void maybeEnter()
1012 {
1013 size_t i = 0;
1014 while (i < RT_ELEMENTS(mLocks))
1015 mLocks[i++].maybeEnter();
1016 }
1017
1018 /**
1019 * Calls AutoWriteLock::enter() methods for all managed semaphore handles in
1020 * order they were passed to the constructor.
1021 */
1022 void enter()
1023 {
1024 size_t i = 0;
1025 while (i < RT_ELEMENTS(mLocks))
1026 mLocks[i++].enter();
1027 }
1028
1029protected:
1030
1031 AutoMultiWriteLockBase() {}
1032
1033 void setLockHandle (size_t aIdx, LockHandle *aHandle)
1034 { mLocks [aIdx].mHandle = aHandle; }
1035
1036private:
1037
1038 AutoWriteLock mLocks [Cnt];
1039
1040 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoMultiWriteLockBase)
1041 DECLARE_CLS_NEW_DELETE_NOOP (AutoMultiWriteLockBase)
1042};
1043
1044/** AutoMultiWriteLockBase <0> is meaningless and forbidden. */
1045template<>
1046class AutoMultiWriteLockBase <0> { private : AutoMultiWriteLockBase(); };
1047
1048/** AutoMultiWriteLockBase <1> is meaningless and forbidden. */
1049template<>
1050class AutoMultiWriteLockBase <1> { private : AutoMultiWriteLockBase(); };
1051
1052////////////////////////////////////////////////////////////////////////////////
1053
1054/* AutoMultiLockN class definitions */
1055
1056#define A(n) LockHandle *l##n
1057#define B(n) setLockHandle (n, l##n)
1058
1059#define C(n) Lockable *l##n
1060#define D(n) setLockHandle (n, l##n ? l##n->lockHandle() : NULL)
1061
1062/**
1063 * AutoMultiWriteLock for 2 locks.
1064 *
1065 * The AutoMultiWriteLockN family of classes provides a possibility to manage
1066 * several read/write semaphores at once. This is handy if all managed
1067 * semaphores need to be locked and unlocked synchronously and will also help to
1068 * avoid locking order errors.
1069 *
1070 * The functionality of the AutoMultiWriteLockN class family is similar to the
1071 * functionality of the AutoMultiLockN class family (see the AutoMultiLock2
1072 * class for details) with two important differences:
1073 * <ol>
1074 * <li>Instances of AutoMultiWriteLockN classes are constructed from a list
1075 * of LockHandle or Lockable arguments directly instead of getting
1076 * intermediate LockOps interface pointers.
1077 * </li>
1078 * <li>All locks are requested in <b>write</b> mode.
1079 * </li>
1080 * <li>Since all locks are requested in write mode, bulk
1081 * AutoMultiWriteLockBase::leave() and AutoMultiWriteLockBase::enter()
1082 * operations are also available, that will leave and enter all managed
1083 * semaphores at once in the proper order (similarly to
1084 * AutoMultiWriteLockBase::lock() and AutoMultiWriteLockBase::unlock()).
1085 * </li>
1086 * </ol>
1087 *
1088 * Here is a typical usage pattern:
1089 * <code>
1090 * ...
1091 * LockHandle data1, data2;
1092 * ...
1093 * {
1094 * AutoMultiWriteLock2 multiLock (&data1, &data2);
1095 * // both locks are held in write mode here
1096 * }
1097 * // both locks are released here
1098 * </code>
1099 */
1100class AutoMultiWriteLock2 : public AutoMultiWriteLockBase <2>
1101{
1102public:
1103 AutoMultiWriteLock2 (A(0), A(1))
1104 { B(0); B(1); acquire(); }
1105 AutoMultiWriteLock2 (C(0), C(1))
1106 { D(0); D(1); acquire(); }
1107};
1108
1109/** AutoMultiWriteLock for 3 locks. See AutoMultiWriteLock2 for more details. */
1110class AutoMultiWriteLock3 : public AutoMultiWriteLockBase <3>
1111{
1112public:
1113 AutoMultiWriteLock3 (A(0), A(1), A(2))
1114 { B(0); B(1); B(2); acquire(); }
1115 AutoMultiWriteLock3 (C(0), C(1), C(2))
1116 { D(0); D(1); D(2); acquire(); }
1117};
1118
1119/** AutoMultiWriteLock for 4 locks. See AutoMultiWriteLock2 for more details. */
1120class AutoMultiWriteLock4 : public AutoMultiWriteLockBase <4>
1121{
1122public:
1123 AutoMultiWriteLock4 (A(0), A(1), A(2), A(3))
1124 { B(0); B(1); B(2); B(3); acquire(); }
1125 AutoMultiWriteLock4 (C(0), C(1), C(2), C(3))
1126 { D(0); D(1); D(2); D(3); acquire(); }
1127};
1128
1129#undef D
1130#undef C
1131#undef B
1132#undef A
1133
1134} /* namespace util */
1135
1136#endif // ____H_AUTOLOCK
1137
1138/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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