VirtualBox

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

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

Main: preparation for deadlock detection: prohibit recursive locking from same AutoLock object (never made sense in the first place), remove dead code

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.5 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.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#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 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
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 cleanup();
404 }
405
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 }
498
499 /**
500 * Same as #leave() but checks if the current thread actally owns the lock
501 * and only proceeds in this case. As a result, as opposed to #leave(),
502 * doesn't assert when called with no lock being held.
503 */
504 void maybeLeave()
505 {
506 if (isWriteLockOnCurrentThread())
507 leave();
508 }
509
510 /**
511 * Same as #enter() but checks if the current thread actally owns the lock
512 * and only proceeds if not. As a result, as opposed to #enter(), doesn't
513 * assert when called with the lock already being held.
514 */
515 void maybeEnter()
516 {
517 if (!isWriteLockOnCurrentThread())
518 enter();
519 }
520
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 }
566
567 /** @see attach (LockHandle *) */
568 void attach(LockHandle &aHandle)
569 {
570 attach(&aHandle);
571 }
572
573 /** @see attach (LockHandle *) */
574 void attach(const Lockable &aLockable)
575 {
576 attach(aLockable.lockHandle());
577 }
578
579 /** @see attach (LockHandle *) */
580 void attach(const Lockable *aLockable)
581 {
582 attach(aLockable ? aLockable->lockHandle() : NULL);
583 }
584
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
613private:
614
615 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoWriteLock)
616 DECLARE_CLS_NEW_DELETE_NOOP (AutoWriteLock)
617
618 template <size_t> friend class AutoMultiWriteLockBase;
619};
620
621////////////////////////////////////////////////////////////////////////////////
622//
623// AutoReadLock
624//
625////////////////////////////////////////////////////////////////////////////////
626
627class AutoReadLock : public AutoLockBase
628{
629public:
630
631 /**
632 * Constructs a null instance that does not manage any read/write
633 * semaphore.
634 *
635 * Note that all method calls on a null instance are no-ops. This allows to
636 * have the code where lock protection can be selected (or omitted) at
637 * runtime.
638 */
639 AutoReadLock()
640 : AutoLockBase(NULL)
641 { }
642
643 /**
644 * Constructs a new instance that will start managing the given read/write
645 * semaphore by requesting a read lock.
646 */
647 AutoReadLock(LockHandle *aHandle)
648 : AutoLockBase(aHandle)
649 {
650 acquire();
651 }
652
653 /**
654 * Constructs a new instance that will start managing the given read/write
655 * semaphore by requesting a read lock.
656 */
657 AutoReadLock(LockHandle &aHandle)
658 : AutoLockBase(&aHandle)
659 {
660 acquire();
661 }
662
663 /**
664 * Constructs a new instance that will start managing the given read/write
665 * semaphore by requesting a read lock.
666 */
667 AutoReadLock(const Lockable &aLockable)
668 : AutoLockBase(aLockable.lockHandle())
669 {
670 acquire();
671 }
672
673 /**
674 * Constructs a new instance that will start managing the given read/write
675 * semaphore by requesting a read lock.
676 */
677 AutoReadLock(const Lockable *aLockable)
678 : AutoLockBase(aLockable ? aLockable->lockHandle() : NULL)
679 {
680 acquire();
681 }
682
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
739private:
740
741 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoReadLock)
742 DECLARE_CLS_NEW_DELETE_NOOP (AutoReadLock)
743};
744
745////////////////////////////////////////////////////////////////////////////////
746//
747// AutoMulti*
748//
749////////////////////////////////////////////////////////////////////////////////
750
751/**
752 * Helper template class for AutoMultiLockN classes.
753 *
754 * @param Cnt number of read/write semaphores to manage.
755 */
756template <size_t Cnt>
757class AutoMultiLockBase
758{
759public:
760
761 /**
762 * Releases all locks if not yet released by #unlock() and destroys the
763 * instance.
764 */
765 ~AutoMultiLockBase()
766 {
767 if (mIsLocked)
768 unlock();
769 }
770
771 /**
772 * Calls LockOps::lock() methods of all managed semaphore handles
773 * in order they were passed to the constructor.
774 *
775 * Note that as opposed to LockHandle::lock(), this call cannot be nested
776 * and will assert if so.
777 */
778 void lock()
779 {
780 AssertReturnVoid (!mIsLocked);
781
782 size_t i = 0;
783 while (i < RT_ELEMENTS (mOps))
784 if (mOps [i])
785 mOps [i ++]->lock();
786 mIsLocked = true;
787 }
788
789 /**
790 * Calls LockOps::unlock() methods of all managed semaphore handles in
791 * reverse to the order they were passed to the constructor.
792 *
793 * Note that as opposed to LockHandle::unlock(), this call cannot be nested
794 * and will assert if so.
795 */
796 void unlock()
797 {
798 AssertReturnVoid (mIsLocked);
799
800 AssertReturnVoid (RT_ELEMENTS (mOps) > 0);
801 size_t i = RT_ELEMENTS (mOps);
802 do
803 if (mOps [-- i])
804 mOps [i]->unlock();
805 while (i != 0);
806 mIsLocked = false;
807 }
808
809protected:
810
811 AutoMultiLockBase() : mIsLocked (false) {}
812
813 LockOps *mOps [Cnt];
814 bool mIsLocked;
815
816private:
817
818 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoMultiLockBase)
819 DECLARE_CLS_NEW_DELETE_NOOP (AutoMultiLockBase)
820};
821
822/** AutoMultiLockBase <0> is meaningless and forbidden. */
823template<>
824class AutoMultiLockBase <0> { private : AutoMultiLockBase(); };
825
826/** AutoMultiLockBase <1> is meaningless and forbidden. */
827template<>
828class AutoMultiLockBase <1> { private : AutoMultiLockBase(); };
829
830////////////////////////////////////////////////////////////////////////////////
831
832/* AutoMultiLockN class definitions */
833
834#define A(n) LockOps *l##n
835#define B(n) mOps [n] = l##n
836
837/**
838 * AutoMultiLock for 2 locks.
839 *
840 * The AutoMultiLockN family of classes provides a possibility to manage several
841 * read/write semaphores at once. This is handy if all managed semaphores need
842 * to be locked and unlocked synchronously and will also help to avoid locking
843 * order errors.
844 *
845 * Instances of AutoMultiLockN classes are constructed from a list of LockOps
846 * arguments. The AutoMultiLockBase::lock() method will make sure that the given
847 * list of semaphores represented by LockOps pointers will be locked in order
848 * they are passed to the constructor. The AutoMultiLockBase::unlock() method
849 * will make sure that they will be unlocked in reverse order.
850 *
851 * The type of the lock to request is specified for each semaphore individually
852 * using the corresponding LockOps getter of a LockHandle or Lockable object:
853 * LockHandle::wlock() in order to request a write lock or LockHandle::rlock()
854 * in order to request a read lock.
855 *
856 * Here is a typical usage pattern:
857 * <code>
858 * ...
859 * LockHandle data1, data2;
860 * ...
861 * {
862 * AutoMultiLock2 multiLock (data1.wlock(), data2.rlock());
863 * // both locks are held here:
864 * // - data1 is locked in write mode (like AutoWriteLock)
865 * // - data2 is locked in read mode (like AutoReadLock)
866 * }
867 * // both locks are released here
868 * </code>
869 */
870class AutoMultiLock2 : public AutoMultiLockBase <2>
871{
872public:
873 AutoMultiLock2 (A(0), A(1))
874 { B(0); B(1); lock(); }
875};
876
877/** AutoMultiLock for 3 locks. See AutoMultiLock2 for more information. */
878class AutoMultiLock3 : public AutoMultiLockBase <3>
879{
880public:
881 AutoMultiLock3 (A(0), A(1), A(2))
882 { B(0); B(1); B(2); lock(); }
883};
884
885/** AutoMultiLock for 4 locks. See AutoMultiLock2 for more information. */
886class AutoMultiLock4 : public AutoMultiLockBase <4>
887{
888public:
889 AutoMultiLock4 (A(0), A(1), A(2), A(3))
890 { B(0); B(1); B(2); B(3); lock(); }
891};
892
893#undef B
894#undef A
895
896////////////////////////////////////////////////////////////////////////////////
897
898/**
899 * Helper template class for AutoMultiWriteLockN classes.
900 *
901 * @param Cnt number of write semaphores to manage.
902 */
903template <size_t Cnt>
904class AutoMultiWriteLockBase
905{
906
907public:
908 /**
909 * Calls AutoWriteLock::acquire() methods for all managed semaphore handles in
910 * order they were passed to the constructor.
911 */
912 void acquire()
913 {
914 size_t i = 0;
915 while (i < RT_ELEMENTS(mLocks))
916 mLocks[i++].acquire();
917 }
918
919 /**
920 * Calls AutoWriteLock::unlock() methods for all managed semaphore handles
921 * in reverse to the order they were passed to the constructor.
922 */
923 void release()
924 {
925 AssertReturnVoid(RT_ELEMENTS(mLocks) > 0);
926 size_t i = RT_ELEMENTS(mLocks);
927 do
928 mLocks[--i].release();
929 while (i != 0);
930 }
931
932 /**
933 * Calls AutoWriteLock::leave() methods for all managed semaphore handles in
934 * reverse to the order they were passed to the constructor.
935 */
936 void leave()
937 {
938 AssertReturnVoid(RT_ELEMENTS(mLocks) > 0);
939 size_t i = RT_ELEMENTS(mLocks);
940 do
941 mLocks[--i].leave();
942 while (i != 0);
943 }
944
945 /**
946 * Calls AutoWriteLock::maybeLeave() methods for all managed semaphore
947 * handles in reverse to the order they were passed to the constructor.
948 */
949 void maybeLeave()
950 {
951 AssertReturnVoid(RT_ELEMENTS(mLocks) > 0);
952 size_t i = RT_ELEMENTS(mLocks);
953 do
954 mLocks [-- i].maybeLeave();
955 while (i != 0);
956 }
957
958 /**
959 * Calls AutoWriteLock::maybeEnter() methods for all managed semaphore
960 * handles in order they were passed to the constructor.
961 */
962 void maybeEnter()
963 {
964 size_t i = 0;
965 while (i < RT_ELEMENTS(mLocks))
966 mLocks[i++].maybeEnter();
967 }
968
969 /**
970 * Calls AutoWriteLock::enter() methods for all managed semaphore handles in
971 * order they were passed to the constructor.
972 */
973 void enter()
974 {
975 size_t i = 0;
976 while (i < RT_ELEMENTS(mLocks))
977 mLocks[i++].enter();
978 }
979
980protected:
981
982 AutoMultiWriteLockBase() {}
983
984 void setLockHandle (size_t aIdx, LockHandle *aHandle)
985 { mLocks [aIdx].mHandle = aHandle; }
986
987private:
988
989 AutoWriteLock mLocks [Cnt];
990
991 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoMultiWriteLockBase)
992 DECLARE_CLS_NEW_DELETE_NOOP (AutoMultiWriteLockBase)
993};
994
995/** AutoMultiWriteLockBase <0> is meaningless and forbidden. */
996template<>
997class AutoMultiWriteLockBase <0> { private : AutoMultiWriteLockBase(); };
998
999/** AutoMultiWriteLockBase <1> is meaningless and forbidden. */
1000template<>
1001class AutoMultiWriteLockBase <1> { private : AutoMultiWriteLockBase(); };
1002
1003////////////////////////////////////////////////////////////////////////////////
1004
1005/* AutoMultiLockN class definitions */
1006
1007#define A(n) LockHandle *l##n
1008#define B(n) setLockHandle (n, l##n)
1009
1010#define C(n) Lockable *l##n
1011#define D(n) setLockHandle (n, l##n ? l##n->lockHandle() : NULL)
1012
1013/**
1014 * AutoMultiWriteLock for 2 locks.
1015 *
1016 * The AutoMultiWriteLockN family of classes provides a possibility to manage
1017 * several read/write semaphores at once. This is handy if all managed
1018 * semaphores need to be locked and unlocked synchronously and will also help to
1019 * avoid locking order errors.
1020 *
1021 * The functionality of the AutoMultiWriteLockN class family is similar to the
1022 * functionality of the AutoMultiLockN class family (see the AutoMultiLock2
1023 * class for details) with two important differences:
1024 * <ol>
1025 * <li>Instances of AutoMultiWriteLockN classes are constructed from a list
1026 * of LockHandle or Lockable arguments directly instead of getting
1027 * intermediate LockOps interface pointers.
1028 * </li>
1029 * <li>All locks are requested in <b>write</b> mode.
1030 * </li>
1031 * <li>Since all locks are requested in write mode, bulk
1032 * AutoMultiWriteLockBase::leave() and AutoMultiWriteLockBase::enter()
1033 * operations are also available, that will leave and enter all managed
1034 * semaphores at once in the proper order (similarly to
1035 * AutoMultiWriteLockBase::lock() and AutoMultiWriteLockBase::unlock()).
1036 * </li>
1037 * </ol>
1038 *
1039 * Here is a typical usage pattern:
1040 * <code>
1041 * ...
1042 * LockHandle data1, data2;
1043 * ...
1044 * {
1045 * AutoMultiWriteLock2 multiLock (&data1, &data2);
1046 * // both locks are held in write mode here
1047 * }
1048 * // both locks are released here
1049 * </code>
1050 */
1051class AutoMultiWriteLock2 : public AutoMultiWriteLockBase <2>
1052{
1053public:
1054 AutoMultiWriteLock2 (A(0), A(1))
1055 { B(0); B(1); acquire(); }
1056 AutoMultiWriteLock2 (C(0), C(1))
1057 { D(0); D(1); acquire(); }
1058};
1059
1060/** AutoMultiWriteLock for 3 locks. See AutoMultiWriteLock2 for more details. */
1061class AutoMultiWriteLock3 : public AutoMultiWriteLockBase <3>
1062{
1063public:
1064 AutoMultiWriteLock3 (A(0), A(1), A(2))
1065 { B(0); B(1); B(2); acquire(); }
1066 AutoMultiWriteLock3 (C(0), C(1), C(2))
1067 { D(0); D(1); D(2); acquire(); }
1068};
1069
1070/** AutoMultiWriteLock for 4 locks. See AutoMultiWriteLock2 for more details. */
1071class AutoMultiWriteLock4 : public AutoMultiWriteLockBase <4>
1072{
1073public:
1074 AutoMultiWriteLock4 (A(0), A(1), A(2), A(3))
1075 { B(0); B(1); B(2); B(3); acquire(); }
1076 AutoMultiWriteLock4 (C(0), C(1), C(2), C(3))
1077 { D(0); D(1); D(2); D(3); acquire(); }
1078};
1079
1080#undef D
1081#undef C
1082#undef B
1083#undef A
1084
1085} /* namespace util */
1086
1087#endif // ____H_AUTOLOCK
1088
1089/* 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