VirtualBox

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

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

Appended vim modeline to set tabstop and expand tabs (in the way
suggested by our coding guidelines).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 44.7 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#ifdef VBOX_MAIN_USE_SEMRW
39# include <iprt/semaphore.h>
40#else
41# ifdef VBOX_MAIN_AUTOLOCK_TRAP
42# include <map>
43# include <list>
44# include <string>
45# endif
46#endif
47
48namespace util
49{
50
51#ifdef VBOX_MAIN_AUTOLOCK_TRAP
52namespace internal
53{
54 struct TLS;
55 DECLCALLBACK(void) TLSDestructor (void *aValue);
56}
57#endif /* VBOX_MAIN_AUTOLOCK_TRAP */
58
59/**
60 * Abstract lock operations. See LockHandle and AutoWriteLock for details.
61 */
62class LockOps
63{
64public:
65
66 virtual ~LockOps() {}
67
68 virtual void lock() = 0;
69 virtual void unlock() = 0;
70};
71
72/**
73 * Read lock operations. See LockHandle and AutoWriteLock for details.
74 */
75class ReadLockOps : public LockOps
76{
77public:
78
79 /**
80 * Requests a read (shared) lock.
81 */
82 virtual void lockRead() = 0;
83
84 /**
85 * Releases a read (shared) lock ackquired by lockRead().
86 */
87 virtual void unlockRead() = 0;
88
89 // LockOps interface
90 void lock() { lockRead(); }
91 void unlock() { unlockRead(); }
92};
93
94/**
95 * Write lock operations. See LockHandle and AutoWriteLock for details.
96 */
97class WriteLockOps : public LockOps
98{
99public:
100
101 /**
102 * Requests a write (exclusive) lock.
103 */
104 virtual void lockWrite() = 0;
105
106 /**
107 * Releases a write (exclusive) lock ackquired by lockWrite().
108 */
109 virtual void unlockWrite() = 0;
110
111 // LockOps interface
112 void lock() { lockWrite(); }
113 void unlock() { unlockWrite(); }
114};
115
116/**
117 * Abstract read/write semaphore handle.
118 *
119 * This is a base class to implement semaphores that provide read/write locking.
120 * Subclasses must implement all pure virtual methods of this class together
121 * with pure methods of ReadLockOps and WriteLockOps classes.
122 *
123 * See the AutoWriteLock class documentation for the detailed description of
124 * read and write locks.
125 */
126class LockHandle : protected ReadLockOps, protected WriteLockOps
127{
128public:
129
130 LockHandle() {}
131 virtual ~LockHandle() {}
132
133 /**
134 * Returns @c true if the current thread holds a write lock on this
135 * read/write semaphore. Intended for debugging only.
136 */
137 virtual bool isWriteLockOnCurrentThread() const = 0;
138
139 /**
140 * Returns the current write lock level of this semaphore. The lock level
141 * determines the number of nested #lock() calls on the given semaphore
142 * handle.
143 *
144 * Note that this call is valid only when the current thread owns a write
145 * lock on the given semaphore handle and will assert otherwise.
146 */
147 virtual uint32_t writeLockLevel() const = 0;
148
149 /**
150 * Returns an interface to read lock operations of this semaphore.
151 * Used by constructors of AutoMultiLockN classes.
152 */
153 LockOps *rlock() { return (ReadLockOps *) this; }
154
155 /**
156 * Returns an interface to write lock operations of this semaphore.
157 * Used by constructors of AutoMultiLockN classes.
158 */
159 LockOps *wlock() { return (WriteLockOps *) this; }
160
161private:
162
163 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (LockHandle)
164
165 friend class AutoWriteLock;
166 friend class AutoReadLock;
167};
168
169/**
170 * Full-featured read/write semaphore handle implementation.
171 *
172 * This is an auxiliary base class for classes that need full-featured
173 * read/write locking as described in the AutoWriteLock class documentation.
174 * Instances of classes inherited from this class can be passed as arguments to
175 * the AutoWriteLock and AutoReadLock constructors.
176 */
177class RWLockHandle : public LockHandle
178{
179public:
180
181 RWLockHandle();
182 virtual ~RWLockHandle();
183
184 bool isWriteLockOnCurrentThread() const;
185
186private:
187
188 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (RWLockHandle)
189
190 void lockWrite();
191 void unlockWrite();
192 void lockRead();
193 void unlockRead();
194
195 uint32_t writeLockLevel() const;
196
197#ifdef VBOX_MAIN_USE_SEMRW
198
199 RTSEMRW mSemRW;
200
201#else /* VBOX_MAIN_USE_SEMRW */
202
203 mutable RTCRITSECT mCritSect;
204 RTSEMEVENT mGoWriteSem;
205 RTSEMEVENTMULTI mGoReadSem;
206
207 RTNATIVETHREAD mWriteLockThread;
208
209 uint32_t mReadLockCount; /*< Number of read locks */
210 uint32_t mSelfReadLockCount; /*< Number of read locks nested in write lock */
211
212 uint32_t mWriteLockLevel;
213 uint32_t mWriteLockPending;
214
215# ifdef VBOX_MAIN_AUTOLOCK_TRAP
216
217 enum Operation { LockRead, UnlockRead, LockWrite, UnlockWrite };
218 void logOp (Operation aOp);
219 void gatherInfo (std::string &aInfo);
220
221 friend DECLCALLBACK(void) internal::TLSDestructor (void *aValue);
222 static void TLSDestructor (internal::TLS *aTLS);
223
224 typedef std::list <std::string> ReaderInfo;
225 ReaderInfo mReaderInfo;
226 std::string mWriterInfo;
227
228# endif /* VBOX_MAIN_AUTOLOCK_TRAP */
229
230#endif /* VBOX_MAIN_USE_SEMRW */
231};
232
233/**
234 * Write-only semaphore handle implementation.
235 *
236 * This is an auxiliary base class for classes that need write-only (exclusive)
237 * locking and do not need read (shared) locking. This implementation uses a
238 * cheap and fast critical section for both lockWrite() and lockRead() methods
239 * which makes a lockRead() call fully equivalent to the lockWrite() call and
240 * therefore makes it pointless to use instahces of this class with
241 * AutoReadLock instances -- shared locking will not be possible anyway and
242 * any call to lock() will block if there are lock owners on other threads.
243 *
244 * Use with care only when absolutely sure that shared locks are not necessary.
245 */
246class WriteLockHandle : public LockHandle
247{
248public:
249
250 WriteLockHandle()
251 {
252 RTCritSectInit (&mCritSect);
253 }
254
255 virtual ~WriteLockHandle()
256 {
257 RTCritSectDelete (&mCritSect);
258 }
259
260 bool isWriteLockOnCurrentThread() const
261 {
262 return RTCritSectIsOwner (&mCritSect);
263 }
264
265private:
266
267 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (WriteLockHandle)
268
269 void lockWrite()
270 {
271#if defined(DEBUG)
272 RTCritSectEnterDebug (&mCritSect,
273 "WriteLockHandle::lockWrite() return address >>>",
274 0, (RTUINTPTR) ASMReturnAddress());
275#else
276 RTCritSectEnter (&mCritSect);
277#endif
278 }
279
280 void unlockWrite()
281 {
282 RTCritSectLeave (&mCritSect);
283 }
284
285 void lockRead() { lockWrite(); }
286 void unlockRead() { unlockWrite(); }
287
288 uint32_t writeLockLevel() const
289 {
290 return RTCritSectGetRecursion (&mCritSect);
291 }
292
293 mutable RTCRITSECT mCritSect;
294};
295
296/**
297 * Lockable interface.
298 *
299 * This is an abstract base for classes that need read/write locking. Unlike
300 * RWLockHandle and other classes that makes the read/write semaphore a part of
301 * class data, this class allows subclasses to decide which semaphore handle to
302 * use.
303 */
304class Lockable
305{
306public:
307
308 /**
309 * Returns a pointer to a LockHandle used by AutoWriteLock/AutoReadLock
310 * for locking. Subclasses are allowed to return @c NULL -- in this case,
311 * the AutoWriteLock/AutoReadLock object constructed using an instance of
312 * such subclass will simply turn into no-op.
313 */
314 virtual LockHandle *lockHandle() const = 0;
315
316 /**
317 * Equivalent to <tt>#lockHandle()->isWriteLockOnCurrentThread()</tt>.
318 * Returns @c false if lockHandle() returns @c NULL.
319 */
320 bool isWriteLockOnCurrentThread()
321 {
322 LockHandle *h = lockHandle();
323 return h ? h->isWriteLockOnCurrentThread() : false;
324 }
325
326 /**
327 * Equivalent to <tt>#lockHandle()->rlock()</tt>.
328 * Returns @c NULL false if lockHandle() returns @c NULL.
329 */
330 LockOps *rlock()
331 {
332 LockHandle *h = lockHandle();
333 return h ? h->rlock() : NULL;
334 }
335
336 /**
337 * Equivalent to <tt>#lockHandle()->wlock()</tt>. Returns @c NULL false if
338 * lockHandle() returns @c NULL.
339 */
340 LockOps *wlock()
341 {
342 LockHandle *h = lockHandle();
343 return h ? h->wlock() : NULL;
344 }
345};
346
347/**
348 * Provides safe management of read/write semaphores in write mode.
349 *
350 * A read/write semaphore is represented by the LockHandle class. This semaphore
351 * can be requested ("locked") in two different modes: for reading and for
352 * writing. A write lock is exclusive and acts like a mutex: only one thread can
353 * acquire a write lock on the given semaphore at a time; all other threads
354 * trying to request a write lock or a read lock (see below) on the same
355 * semaphore will be indefinitely blocked until the owning thread releases the
356 * write lock.
357 *
358 * A read lock is shared. This means that several threads can acquire a read
359 * lock on the same semaphore at the same time provided that there is no thread
360 * that holds a write lock on that semaphore. Note that when there are one or
361 * more threads holding read locks, a request for a write lock on another thread
362 * will be indefinitely blocked until all threads holding read locks release
363 * them.
364 *
365 * Note that write locks can be nested -- the same thread can request a write
366 * lock on the same semaphore several times. In this case, the corresponding
367 * number of release calls must be done in order to completely release all
368 * nested write locks and make the semaphore available for locking by other
369 * threads.
370 *
371 * Read locks can be nested too in which case the same rule of the equal number
372 * of the release calls applies. Read locks can be also nested into write
373 * locks which means that the same thread can successfully request a read lock
374 * if it already holds a write lock. However, please note that the opposite is
375 * <b>not possible</b>: if a thread tries to request a write lock on the same
376 * semaphore it is already holding a read lock, it will definitely produce a
377 * <b>deadlock</b> (i.e. it will block forever waiting for itself).
378 *
379 * Note that instances of the AutoWriteLock class manage write locks of
380 * read/write semaphores only. In order to manage read locks, please use the
381 * AutoReadLock class.
382 *
383 * Safe semaphore management consists of the following:
384 * <ul>
385 * <li>When an instance of the AutoWriteLock class is constructed given a
386 * valid semaphore handle, it will automatically request a write lock on that
387 * semaphore.
388 * </li>
389 * <li>When an instance of the AutoWriteLock class constructed given a valid
390 * semaphore handle is destroyed (e.g. goes out of scope), it will
391 * automatically release the write lock that was requested upon construction
392 * and also all nested write locks requested later using the #lock() call
393 * (note that the latter is considered to be a program logic error, see the
394 * #~AutoWriteLock() description for details).
395 * </li>
396 * </ul>
397 *
398 * Note that the LockHandle class taken by AutoWriteLock constructors is an
399 * abstract base of the read/write semaphore. You should choose one of the
400 * existing subclasses of this abstract class or create your own subclass that
401 * implements necessary read and write lock semantics. The most suitable choice
402 * is the RWLockHandle class which provides full support for both read and write
403 * locks as describerd above. Alternatively, you can use the WriteLockHandle
404 * class if you only need write (exclusive) locking (WriteLockHandle requires
405 * less system resources and works faster).
406 *
407 * A typical usage pattern of the AutoWriteLock class is as follows:
408 * <code>
409 * struct Struct : public RWLockHandle
410 * {
411 * ...
412 * };
413 *
414 * void foo (Struct &aStruct)
415 * {
416 * {
417 * // acquire a write lock of aStruct
418 * AutoWriteLock alock (aStruct);
419 *
420 * // now we can modify aStruct in a thread-safe manner
421 * aStruct.foo = ...;
422 *
423 * // note that the write lock will be automatically released upon
424 * // execution of the return statement below
425 * if (!aStruct.bar)
426 * return;
427 *
428 * ...
429 * }
430 *
431 * // note that the write lock is automatically released here
432 * }
433 * </code>
434 *
435 * <b>Locking policy</b>
436 *
437 * When there are multiple threads and multiple objects to lock, there is always
438 * a potential possibility to produce a deadlock if the lock order is mixed up.
439 * Here is a classical example of a deadlock when two threads need to lock the
440 * same two objects in a row but do it in different order:
441 * <code>
442 * Thread 1:
443 * #1: AutoWriteLock (mFoo);
444 * ...
445 * #2: AutoWriteLock (mBar);
446 * ...
447 * Thread 2:
448 * #3: AutoWriteLock (mBar);
449 * ...
450 * #4: AutoWriteLock (mFoo);
451 * ...
452 * </code>
453 *
454 * If the threads happen to be scheduled so that #3 completes after #1 has
455 * completed but before #2 got control, the threads will hit a deadlock: Thread
456 * 2 will be holding mBar and waiting for mFoo at #4 forever because Thread 1 is
457 * holding mFoo and won't release it until it acquires mBar at #2 that will
458 * never happen because mBar is held by Thread 2.
459 *
460 * One of ways to avoid the described behavior is to never lock more than one
461 * obhect in a row. While it is definitely a good and safe practice, it's not
462 * always possible: the application logic may require several simultaneous locks
463 * in order to provide data integrity.
464 *
465 * One of the possibilities to solve the deadlock problem is to make sure that
466 * the locking order is always the same across the application. In the above
467 * example, it would mean that <b>both</b> threads should first requiest a lock
468 * of mFoo and then mBar (or vice versa). One of the methods to guarantee the
469 * locking order consistent is to introduce a set of locking rules. The
470 * advantage of this method is that it doesn't require any special semaphore
471 * implementation or additional control structures. The disadvantage is that
472 * it's the programmer who must make sure these rules are obeyed across the
473 * whole application so the human factor applies. Taking the simplicity of this
474 * method into account, it is chosen to solve potential deadlock problems when
475 * using AutoWriteLock and AutoReadLock classes. Here are the locking rules
476 * that must be obeyed by <b>all</b> users of these classes. Note that if more
477 * than one rule matches the given group of objects to lock, all of these rules
478 * must be met:
479 * <ol>
480 * <li>If there is a parent-child (or master-slave) relationship between the
481 * locked objects, parent (master) objects must be locked before child
482 * (slave) objects.
483 * </li>
484 * <li>When a group of equal objects (in terms of parent-child or
485 * master-slave relationsip) needs to be locked in a raw, the lock order
486 * must match the sort order (which must be consistent for the given group).
487 * </ol>
488 * Note that if there is no pragrammatically expressed sort order (e.g.
489 * the objects are not part of the sorted vector or list but instead are
490 * separate data members of a class), object class names sorted in alphabetical
491 * order must be used to determine the lock order. If there is more than one
492 * object of the given class, the object variable names' alphabetical order must
493 * be used as a lock order. When objects are not represented as individual
494 * variables, as in case of unsorted arrays/lists, the list of alphabetically
495 * sorted object UUIDs must be used to determine the sort order.
496 *
497 * All non-standard locking order must be avoided by all means, but when
498 * absolutely necessary, it must be clearly documented at relevant places so it
499 * is well seen by other developers. For example, if a set of instances of some
500 * class needs to be locked but these instances are not part of the sorted list
501 * and don't have UUIDs, then the class description must state what to use to
502 * determine the lock order (maybe some property that returns an unique value
503 * per every object).
504 */
505class AutoWriteLock
506{
507public:
508
509 /**
510 * Constructs a null instance that does not manage any read/write
511 * semaphore.
512 *
513 * Note that all method calls on a null instance are no-ops. This allows to
514 * have the code where lock protection can be selected (or omitted) at
515 * runtime.
516 */
517 AutoWriteLock() : mHandle (NULL), mLockLevel (0), mGlobalLockLevel (0) {}
518
519 /**
520 * Constructs a new instance that will start managing the given read/write
521 * semaphore by requesting a write lock.
522 */
523 AutoWriteLock (LockHandle *aHandle)
524 : mHandle (aHandle), mLockLevel (0), mGlobalLockLevel (0)
525 { lock(); }
526
527 /**
528 * Constructs a new instance that will start managing the given read/write
529 * semaphore by requesting a write lock.
530 */
531 AutoWriteLock (LockHandle &aHandle)
532 : mHandle (&aHandle), mLockLevel (0), mGlobalLockLevel (0)
533 { lock(); }
534
535 /**
536 * Constructs a new instance that will start managing the given read/write
537 * semaphore by requesting a write lock.
538 */
539 AutoWriteLock (const Lockable &aLockable)
540 : mHandle (aLockable.lockHandle()), mLockLevel (0), mGlobalLockLevel (0)
541 { lock(); }
542
543 /**
544 * Constructs a new instance that will start managing the given read/write
545 * semaphore by requesting a write lock.
546 */
547 AutoWriteLock (const Lockable *aLockable)
548 : mHandle (aLockable ? aLockable->lockHandle() : NULL)
549 , mLockLevel (0), mGlobalLockLevel (0)
550 { lock(); }
551
552 /**
553 * Release all write locks acquired by this instance through the #lock()
554 * call and destroys the instance.
555 *
556 * Note that if there there are nested #lock() calls without the
557 * corresponding number of #unlock() calls when the destructor is called, it
558 * will assert. This is because having an unbalanced number of nested locks
559 * is a program logic error which must be fixed.
560 */
561 ~AutoWriteLock()
562 {
563 if (mHandle)
564 {
565 if (mGlobalLockLevel)
566 {
567 mGlobalLockLevel -= mLockLevel;
568 mLockLevel = 0;
569 for (; mGlobalLockLevel; -- mGlobalLockLevel)
570 mHandle->lockWrite();
571 }
572
573 AssertMsg (mLockLevel <= 1, ("Lock level > 1: %d\n", mLockLevel));
574 for (; mLockLevel; -- mLockLevel)
575 mHandle->unlockWrite();
576 }
577 }
578
579 /**
580 * Requests a write (exclusive) lock. If a write lock is already owned by
581 * this thread, increases the lock level (allowing for nested write locks on
582 * the same thread). Blocks indefinitely if a write lock or a read lock is
583 * already owned by another thread until that tread releases the locks,
584 * otherwise returns immediately.
585 */
586 void lock()
587 {
588 if (mHandle)
589 {
590 mHandle->lockWrite();
591 ++ mLockLevel;
592 Assert (mLockLevel != 0 /* overflow? */);
593 }
594 }
595
596 /**
597 * Decreases the write lock level increased by #lock(). If the level drops
598 * to zero (e.g. the number of nested #unlock() calls matches the number of
599 * nested #lock() calls), releases the lock making the managed semaphore
600 * available for locking by other threads.
601 */
602 void unlock()
603 {
604 if (mHandle)
605 {
606 AssertReturnVoid (mLockLevel != 0 /* unlock() w/o preceding lock()? */);
607 mHandle->unlockWrite();
608 -- mLockLevel;
609 }
610 }
611
612 /**
613 * Causes the current thread to completely release the write lock to make
614 * the managed semaphore immediately available for locking by other threads.
615 *
616 * This implies that all nested write locks on the semaphore will be
617 * released, even those that were acquired through the calls to #lock()
618 * methods of all other AutoWriteLock/AutoReadLock instances managing the
619 * <b>same</b> read/write semaphore.
620 *
621 * After calling this method, the only method you are allowed to call is
622 * #enter(). It will acquire the write lock again and restore the same
623 * level of nesting as it had before calling #leave().
624 *
625 * If this instance is destroyed without calling #enter(), the destructor
626 * will try to restore the write lock level that existed when #leave() was
627 * called minus the number of nested #lock() calls made on this instance
628 * itself. This is done to preserve lock levels of other
629 * AutoWriteLock/AutoReadLock instances managing the same semaphore (if
630 * any). Tiis also means that the destructor may indefinitely block if a
631 * write or a read lock is owned by some other thread by that time.
632 */
633 void leave()
634 {
635 if (mHandle)
636 {
637 AssertReturnVoid (mLockLevel != 0 /* leave() w/o preceding lock()? */);
638 AssertReturnVoid (mGlobalLockLevel == 0 /* second leave() in a row? */);
639
640 mGlobalLockLevel = mHandle->writeLockLevel();
641 AssertReturnVoid (mGlobalLockLevel >= mLockLevel /* logic error! */);
642
643 for (uint32_t left = mGlobalLockLevel; left; -- left)
644 mHandle->unlockWrite();
645 }
646 }
647
648 /**
649 * Same as #leave() but checks if the current thread actally owns the lock
650 * and only proceeds in this case. As a result, as opposed to #leave(),
651 * doesn't assert when called with no lock being held.
652 */
653 void maybeLeave()
654 {
655 if (isWriteLockOnCurrentThread())
656 leave();
657 }
658
659 /**
660 * Same as #enter() but checks if the current thread actally owns the lock
661 * and only proceeds if not. As a result, as opposed to #enter(), doesn't
662 * assert when called with the lock already being held.
663 */
664 void maybeEnter()
665 {
666 if (!isWriteLockOnCurrentThread())
667 enter();
668 }
669
670 /**
671 * Causes the current thread to restore the write lock level after the
672 * #leave() call. This call will indefinitely block if another thread has
673 * successfully acquired a write or a read lock on the same semaphore in
674 * between.
675 */
676 void enter()
677 {
678 if (mHandle)
679 {
680 AssertReturnVoid (mLockLevel != 0 /* enter() w/o preceding lock()+leave()? */);
681 AssertReturnVoid (mGlobalLockLevel != 0 /* enter() w/o preceding leave()? */);
682
683 for (; mGlobalLockLevel; -- mGlobalLockLevel)
684 mHandle->lockWrite();
685 }
686 }
687
688 /**
689 * Attaches another handle to this auto lock instance.
690 *
691 * The previous object's lock is completely released before the new one is
692 * acquired. The lock level of the new handle will be the same. This
693 * also means that if the lock was not acquired at all before #attach(), it
694 * will not be acquired on the new handle too.
695 *
696 * @param aHandle New handle to attach.
697 */
698 void attach (LockHandle *aHandle)
699 {
700 /* detect simple self-reattachment */
701 if (mHandle != aHandle)
702 {
703 uint32_t lockLevel = mLockLevel;
704
705 /* perform the destructor part */
706 if (mHandle)
707 {
708 if (mGlobalLockLevel)
709 {
710 mGlobalLockLevel -= mLockLevel;
711 mLockLevel = 0;
712 for (; mGlobalLockLevel; -- mGlobalLockLevel)
713 mHandle->lockWrite();
714 }
715
716 AssertMsg (mLockLevel <= 1, ("Lock level > 1: %d\n", mLockLevel));
717 for (; mLockLevel; -- mLockLevel)
718 mHandle->unlockWrite();
719 }
720
721 mHandle = aHandle;
722 mLockLevel = lockLevel;
723
724 if (mHandle)
725 for (; lockLevel; -- lockLevel)
726 mHandle->lockWrite();
727 }
728 }
729
730 /** @see attach (LockHandle *) */
731 void attach (LockHandle &aHandle) { attach (&aHandle); }
732
733 /** @see attach (LockHandle *) */
734 void attach (const Lockable &aLockable) { attach (aLockable.lockHandle()); }
735
736 /** @see attach (LockHandle *) */
737 void attach (const Lockable *aLockable)
738 { attach (aLockable ? aLockable->lockHandle() : NULL); }
739
740 /** Verbose equivalent to <tt>attach (NULL)</tt>. */
741 void detach() { attach ((LockHandle *) NULL); }
742
743 /** Returns @c true if this instance manages a null semaphore handle. */
744 bool isNull() const { return mHandle == NULL; }
745 bool operator !() const { return isNull(); }
746
747 /**
748 * Returns @c true if the current thread holds a write lock on the managed
749 * read/write semaphore. Returns @c false if the managed semaphore is @c
750 * NULL.
751 *
752 * @note Intended for debugging only.
753 */
754 bool isWriteLockOnCurrentThread() const
755 {
756 return mHandle ? mHandle->isWriteLockOnCurrentThread() : false;
757 }
758
759 /**
760 * Returns the current write lock level of the managed smaphore. The lock
761 * level determines the number of nested #lock() calls on the given
762 * semaphore handle. Returns @c 0 if the managed semaphore is @c
763 * NULL.
764 *
765 * Note that this call is valid only when the current thread owns a write
766 * lock on the given semaphore handle and will assert otherwise.
767 *
768 * @note Intended for debugging only.
769 */
770 uint32_t writeLockLevel() const
771 {
772 return mHandle ? mHandle->writeLockLevel() : 0;
773 }
774
775 /**
776 * Returns @c true if this instance manages the given semaphore handle.
777 *
778 * @note Intended for debugging only.
779 */
780 bool belongsTo (const LockHandle &aHandle) const { return mHandle == &aHandle; }
781
782 /**
783 * Returns @c true if this instance manages the given semaphore handle.
784 *
785 * @note Intended for debugging only.
786 */
787 bool belongsTo (const LockHandle *aHandle) const { return mHandle == aHandle; }
788
789 /**
790 * Returns @c true if this instance manages the given lockable object.
791 *
792 * @note Intended for debugging only.
793 */
794 bool belongsTo (const Lockable &aLockable)
795 {
796 return belongsTo (aLockable.lockHandle());
797 }
798
799 /**
800 * Returns @c true if this instance manages the given lockable object.
801 *
802 * @note Intended for debugging only.
803 */
804 bool belongsTo (const Lockable *aLockable)
805 {
806 return aLockable && belongsTo (aLockable->lockHandle());
807 }
808
809private:
810
811 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoWriteLock)
812 DECLARE_CLS_NEW_DELETE_NOOP (AutoWriteLock)
813
814 LockHandle *mHandle;
815 uint32_t mLockLevel;
816 uint32_t mGlobalLockLevel;
817
818 template <size_t> friend class AutoMultiWriteLockBase;
819};
820
821////////////////////////////////////////////////////////////////////////////////
822
823/**
824 * Provides safe management of read/write semaphores in read mode.
825 *
826 * This class differs from the AutoWriteLock class is so that it's #lock() and
827 * #unlock() methods requests and release read (shared) locks on the managed
828 * read/write semaphore instead of write (exclusive) locks. See the
829 * AutoWriteLock class description for more information about read and write
830 * locks.
831 *
832 * Safe semaphore management consists of the following:
833 * <ul>
834 * <li>When an instance of the AutoReadLock class is constructed given a
835 * valid semaphore handle, it will automatically request a read lock on that
836 * semaphore.
837 * </li>
838 * <li>When an instance of the AutoReadLock class constructed given a valid
839 * semaphore handle is destroyed (e.g. goes out of scope), it will
840 * automatically release the read lock that was requested upon construction
841 * and also all nested read locks requested later using the #lock() call (note
842 * that the latter is considered to be a program logic error, see the
843 * #~AutoReadLock() description for details).
844 * </li>
845 * </ul>
846 *
847 * Note that the LockHandle class taken by AutoReadLock constructors is an
848 * abstract base of the read/write semaphore. You should choose one of the
849 * existing subclasses of this abstract class or create your own subclass that
850 * implements necessary read and write lock semantics. The most suitable choice
851 * is the RWLockHandle class which provides full support for both read and write
852 * locks as describerd in AutoWriteLock docs. Alternatively, you can use the
853 * WriteLockHandle class if you only need write (exclusive) locking
854 * (WriteLockHandle requires less system resources and works faster).
855 *
856 * However, please note that it absolutely does not make sense to manage
857 * WriteLockHandle semaphores with AutoReadLock instances because
858 * AutoReadLock instances will behave like AutoWriteLock instances in this
859 * case since WriteLockHandle provides only exclusive write locking. You have
860 * been warned.
861
862 * A typical usage pattern of the AutoReadLock class is as follows:
863 * <code>
864 * struct Struct : public RWLockHandle
865 * {
866 * ...
867 * };
868 *
869 * void foo (Struct &aStruct)
870 * {
871 * {
872 * // acquire a read lock of aStruct (note that two foo() calls may be
873 * executed on separate threads simultaneously w/o blocking each other)
874 * AutoReadLock alock (aStruct);
875 *
876 * // now we can read aStruct in a thread-safe manner
877 * if (aStruct.foo)
878 * ...;
879 *
880 * // note that the read lock will be automatically released upon
881 * // execution of the return statement below
882 * if (!aStruct.bar)
883 * return;
884 *
885 * ...
886 * }
887 *
888 * // note that the read lock is automatically released here
889 * }
890 * </code>
891 */
892class AutoReadLock
893{
894public:
895
896 /**
897 * Constructs a null instance that does not manage any read/write
898 * semaphore.
899 *
900 * Note that all method calls on a null instance are no-ops. This allows to
901 * have the code where lock protection can be selected (or omitted) at
902 * runtime.
903 */
904 AutoReadLock() : mHandle (NULL), mLockLevel (0) {}
905
906 /**
907 * Constructs a new instance that will start managing the given read/write
908 * semaphore by requesting a read lock.
909 */
910 AutoReadLock (LockHandle *aHandle)
911 : mHandle (aHandle), mLockLevel (0)
912 { lock(); }
913
914 /**
915 * Constructs a new instance that will start managing the given read/write
916 * semaphore by requesting a read lock.
917 */
918 AutoReadLock (LockHandle &aHandle)
919 : mHandle (&aHandle), mLockLevel (0)
920 { lock(); }
921
922 /**
923 * Constructs a new instance that will start managing the given read/write
924 * semaphore by requesting a read lock.
925 */
926 AutoReadLock (const Lockable &aLockable)
927 : mHandle (aLockable.lockHandle()), mLockLevel (0)
928 { lock(); }
929
930 /**
931 * Constructs a new instance that will start managing the given read/write
932 * semaphore by requesting a read lock.
933 */
934 AutoReadLock (const Lockable *aLockable)
935 : mHandle (aLockable ? aLockable->lockHandle() : NULL)
936 , mLockLevel (0)
937 { lock(); }
938
939 /**
940 * Release all read locks acquired by this instance through the #lock()
941 * call and destroys the instance.
942 *
943 * Note that if there there are nested #lock() calls without the
944 * corresponding number of #unlock() calls when the destructor is called, it
945 * will assert. This is because having an unbalanced number of nested locks
946 * is a program logic error which must be fixed.
947 */
948 ~AutoReadLock()
949 {
950 if (mHandle)
951 {
952 AssertMsg (mLockLevel <= 1, ("Lock level > 1: %d\n", mLockLevel));
953 for (; mLockLevel; -- mLockLevel)
954 mHandle->unlockRead();
955 }
956 }
957
958 /**
959 * Requests a read (shared) lock. If a read lock is already owned by
960 * this thread, increases the lock level (allowing for nested read locks on
961 * the same thread). Blocks indefinitely if a write lock is already owned by
962 * another thread until that tread releases the write lock, otherwise
963 * returns immediately.
964 *
965 * Note that this method returns immediately even if any number of other
966 * threads owns read locks on the same semaphore. Also returns immediately
967 * if a write lock on this semaphore is owned by the current thread which
968 * allows for read locks nested into write locks on the same thread.
969 */
970 void lock()
971 {
972 if (mHandle)
973 {
974 mHandle->lockRead();
975 ++ mLockLevel;
976 Assert (mLockLevel != 0 /* overflow? */);
977 }
978 }
979
980 /**
981 * Decreases the read lock level increased by #lock(). If the level drops to
982 * zero (e.g. the number of nested #unlock() calls matches the number of
983 * nested #lock() calls), releases the lock making the managed semaphore
984 * available for locking by other threads.
985 */
986 void unlock()
987 {
988 if (mHandle)
989 {
990 AssertReturnVoid (mLockLevel != 0 /* unlock() w/o preceding lock()? */);
991 mHandle->unlockRead();
992 -- mLockLevel;
993 }
994 }
995
996 /**
997 * Attaches another handle to this auto lock instance.
998 *
999 * The previous object's lock is completely released before the new one is
1000 * acquired. The lock level of the new handle will be the same. This also
1001 * means that if the lock was not acquired at all before #attach(), it will
1002 * not be acquired on the new handle too.
1003 *
1004 * @param aHandle New handle to attach.
1005 */
1006 void attach (LockHandle *aHandle)
1007 {
1008 /* detect simple self-reattachment */
1009 if (mHandle != aHandle)
1010 {
1011 uint32_t lockLevel = mLockLevel;
1012 if (mHandle)
1013 for (; mLockLevel; -- mLockLevel)
1014 mHandle->unlockRead();
1015 mHandle = aHandle;
1016 mLockLevel = lockLevel;
1017 if (mHandle)
1018 for (; lockLevel; -- lockLevel)
1019 mHandle->lockRead();
1020 }
1021 }
1022
1023 /** @see attach (LockHandle *) */
1024 void attach (LockHandle &aHandle) { attach (&aHandle); }
1025
1026 /** @see attach (LockHandle *) */
1027 void attach (const Lockable &aLockable) { attach (aLockable.lockHandle()); }
1028
1029 /** @see attach (LockHandle *) */
1030 void attach (const Lockable *aLockable)
1031 { attach (aLockable ? aLockable->lockHandle() : NULL); }
1032
1033 /** Verbose equivalent to <tt>attach (NULL)</tt>. */
1034 void detach() { attach ((LockHandle *) NULL); }
1035
1036 /** Returns @c true if this instance manages a null semaphore handle. */
1037 bool isNull() const { return mHandle == NULL; }
1038 bool operator !() const { return isNull(); }
1039
1040 /**
1041 * Returns @c true if this instance manages the given semaphore handle.
1042 *
1043 * @note Intended for debugging only.
1044 */
1045 bool belongsTo (const LockHandle &aHandle) const { return mHandle == &aHandle; }
1046
1047 /**
1048 * Returns @c true if this instance manages the given semaphore handle.
1049 *
1050 * @note Intended for debugging only.
1051 */
1052 bool belongsTo (const LockHandle *aHandle) const { return mHandle == aHandle; }
1053
1054 /**
1055 * Returns @c true if this instance manages the given lockable object.
1056 *
1057 * @note Intended for debugging only.
1058 */
1059 bool belongsTo (const Lockable &aLockable)
1060 {
1061 return belongsTo (aLockable.lockHandle());
1062 }
1063
1064 /**
1065 * Returns @c true if this instance manages the given lockable object.
1066 *
1067 * @note Intended for debugging only.
1068 */
1069 bool belongsTo (const Lockable *aLockable)
1070 {
1071 return aLockable && belongsTo (aLockable->lockHandle());
1072 }
1073
1074private:
1075
1076 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoReadLock)
1077 DECLARE_CLS_NEW_DELETE_NOOP (AutoReadLock)
1078
1079 LockHandle *mHandle;
1080 uint32_t mLockLevel;
1081};
1082
1083////////////////////////////////////////////////////////////////////////////////
1084
1085/**
1086 * Helper template class for AutoMultiLockN classes.
1087 *
1088 * @param Cnt number of read/write semaphores to manage.
1089 */
1090template <size_t Cnt>
1091class AutoMultiLockBase
1092{
1093public:
1094
1095 /**
1096 * Releases all locks if not yet released by #unlock() and destroys the
1097 * instance.
1098 */
1099 ~AutoMultiLockBase()
1100 {
1101 if (mIsLocked)
1102 unlock();
1103 }
1104
1105 /**
1106 * Calls LockOps::lock() methods of all managed semaphore handles
1107 * in order they were passed to the constructor.
1108 *
1109 * Note that as opposed to LockHandle::lock(), this call cannot be nested
1110 * and will assert if so.
1111 */
1112 void lock()
1113 {
1114 AssertReturnVoid (!mIsLocked);
1115
1116 size_t i = 0;
1117 while (i < RT_ELEMENTS (mOps))
1118 if (mOps [i])
1119 mOps [i ++]->lock();
1120 mIsLocked = true;
1121 }
1122
1123 /**
1124 * Calls LockOps::unlock() methods of all managed semaphore handles in
1125 * reverse to the order they were passed to the constructor.
1126 *
1127 * Note that as opposed to LockHandle::unlock(), this call cannot be nested
1128 * and will assert if so.
1129 */
1130 void unlock()
1131 {
1132 AssertReturnVoid (mIsLocked);
1133
1134 AssertReturnVoid (RT_ELEMENTS (mOps) > 0);
1135 size_t i = RT_ELEMENTS (mOps);
1136 do
1137 if (mOps [-- i])
1138 mOps [i]->unlock();
1139 while (i != 0);
1140 mIsLocked = false;
1141 }
1142
1143protected:
1144
1145 AutoMultiLockBase() : mIsLocked (false) {}
1146
1147 LockOps *mOps [Cnt];
1148 bool mIsLocked;
1149
1150private:
1151
1152 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoMultiLockBase)
1153 DECLARE_CLS_NEW_DELETE_NOOP (AutoMultiLockBase)
1154};
1155
1156/** AutoMultiLockBase <0> is meaningless and forbidden. */
1157template<>
1158class AutoMultiLockBase <0> { private : AutoMultiLockBase(); };
1159
1160/** AutoMultiLockBase <1> is meaningless and forbidden. */
1161template<>
1162class AutoMultiLockBase <1> { private : AutoMultiLockBase(); };
1163
1164////////////////////////////////////////////////////////////////////////////////
1165
1166/* AutoMultiLockN class definitions */
1167
1168#define A(n) LockOps *l##n
1169#define B(n) mOps [n] = l##n
1170
1171/**
1172 * AutoMultiLock for 2 locks.
1173 *
1174 * The AutoMultiLockN family of classes provides a possibility to manage several
1175 * read/write semaphores at once. This is handy if all managed semaphores need
1176 * to be locked and unlocked synchronously and will also help to avoid locking
1177 * order errors.
1178 *
1179 * Instances of AutoMultiLockN classes are constructed from a list of LockOps
1180 * arguments. The AutoMultiLockBase::lock() method will make sure that the given
1181 * list of semaphores represented by LockOps pointers will be locked in order
1182 * they are passed to the constructor. The AutoMultiLockBase::unlock() method
1183 * will make sure that they will be unlocked in reverse order.
1184 *
1185 * The type of the lock to request is specified for each semaphore individually
1186 * using the corresponding LockOps getter of a LockHandle or Lockable object:
1187 * LockHandle::wlock() in order to request a write lock or LockHandle::rlock()
1188 * in order to request a read lock.
1189 *
1190 * Here is a typical usage pattern:
1191 * <code>
1192 * ...
1193 * LockHandle data1, data2;
1194 * ...
1195 * {
1196 * AutoMultiLock2 multiLock (data1.wlock(), data2.rlock());
1197 * // both locks are held here:
1198 * // - data1 is locked in write mode (like AutoWriteLock)
1199 * // - data2 is locked in read mode (like AutoReadLock)
1200 * }
1201 * // both locks are released here
1202 * </code>
1203 */
1204class AutoMultiLock2 : public AutoMultiLockBase <2>
1205{
1206public:
1207 AutoMultiLock2 (A(0), A(1))
1208 { B(0); B(1); lock(); }
1209};
1210
1211/** AutoMultiLock for 3 locks. See AutoMultiLock2 for more information. */
1212class AutoMultiLock3 : public AutoMultiLockBase <3>
1213{
1214public:
1215 AutoMultiLock3 (A(0), A(1), A(2))
1216 { B(0); B(1); B(2); lock(); }
1217};
1218
1219/** AutoMultiLock for 4 locks. See AutoMultiLock2 for more information. */
1220class AutoMultiLock4 : public AutoMultiLockBase <4>
1221{
1222public:
1223 AutoMultiLock4 (A(0), A(1), A(2), A(3))
1224 { B(0); B(1); B(2); B(3); lock(); }
1225};
1226
1227#undef B
1228#undef A
1229
1230////////////////////////////////////////////////////////////////////////////////
1231
1232/**
1233 * Helper template class for AutoMultiWriteLockN classes.
1234 *
1235 * @param Cnt number of write semaphores to manage.
1236 */
1237template <size_t Cnt>
1238class AutoMultiWriteLockBase
1239{
1240public:
1241
1242 /**
1243 * Calls AutoWriteLock::lock() methods for all managed semaphore handles in
1244 * order they were passed to the constructor.
1245 */
1246 void lock()
1247 {
1248 size_t i = 0;
1249 while (i < RT_ELEMENTS (mLocks))
1250 mLocks [i ++].lock();
1251 }
1252
1253 /**
1254 * Calls AutoWriteLock::unlock() methods for all managed semaphore handles
1255 * in reverse to the order they were passed to the constructor.
1256 */
1257 void unlock()
1258 {
1259 AssertReturnVoid (RT_ELEMENTS (mLocks) > 0);
1260 size_t i = RT_ELEMENTS (mLocks);
1261 do
1262 mLocks [-- i].unlock();
1263 while (i != 0);
1264 }
1265
1266 /**
1267 * Calls AutoWriteLock::leave() methods for all managed semaphore handles in
1268 * reverse to the order they were passed to the constructor.
1269 */
1270 void leave()
1271 {
1272 AssertReturnVoid (RT_ELEMENTS (mLocks) > 0);
1273 size_t i = RT_ELEMENTS (mLocks);
1274 do
1275 mLocks [-- i].leave();
1276 while (i != 0);
1277 }
1278
1279 /**
1280 * Calls AutoWriteLock::maybeLeave() methods for all managed semaphore
1281 * handles in reverse to the order they were passed to the constructor.
1282 */
1283 void maybeLeave()
1284 {
1285 AssertReturnVoid (RT_ELEMENTS (mLocks) > 0);
1286 size_t i = RT_ELEMENTS (mLocks);
1287 do
1288 mLocks [-- i].maybeLeave();
1289 while (i != 0);
1290 }
1291
1292 /**
1293 * Calls AutoWriteLock::maybeEnter() methods for all managed semaphore
1294 * handles in order they were passed to the constructor.
1295 */
1296 void maybeEnter()
1297 {
1298 size_t i = 0;
1299 while (i < RT_ELEMENTS (mLocks))
1300 mLocks [i ++].maybeEnter();
1301 }
1302
1303 /**
1304 * Calls AutoWriteLock::enter() methods for all managed semaphore handles in
1305 * order they were passed to the constructor.
1306 */
1307 void enter()
1308 {
1309 size_t i = 0;
1310 while (i < RT_ELEMENTS (mLocks))
1311 mLocks [i ++].enter();
1312 }
1313
1314protected:
1315
1316 AutoMultiWriteLockBase() {}
1317
1318 void setLockHandle (size_t aIdx, LockHandle *aHandle)
1319 { mLocks [aIdx].mHandle = aHandle; }
1320
1321private:
1322
1323 AutoWriteLock mLocks [Cnt];
1324
1325 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoMultiWriteLockBase)
1326 DECLARE_CLS_NEW_DELETE_NOOP (AutoMultiWriteLockBase)
1327};
1328
1329/** AutoMultiWriteLockBase <0> is meaningless and forbidden. */
1330template<>
1331class AutoMultiWriteLockBase <0> { private : AutoMultiWriteLockBase(); };
1332
1333/** AutoMultiWriteLockBase <1> is meaningless and forbidden. */
1334template<>
1335class AutoMultiWriteLockBase <1> { private : AutoMultiWriteLockBase(); };
1336
1337////////////////////////////////////////////////////////////////////////////////
1338
1339/* AutoMultiLockN class definitions */
1340
1341#define A(n) LockHandle *l##n
1342#define B(n) setLockHandle (n, l##n)
1343
1344#define C(n) Lockable *l##n
1345#define D(n) setLockHandle (n, l##n ? l##n->lockHandle() : NULL)
1346
1347/**
1348 * AutoMultiWriteLock for 2 locks.
1349 *
1350 * The AutoMultiWriteLockN family of classes provides a possibility to manage
1351 * several read/write semaphores at once. This is handy if all managed
1352 * semaphores need to be locked and unlocked synchronously and will also help to
1353 * avoid locking order errors.
1354 *
1355 * The functionality of the AutoMultiWriteLockN class family is similar to the
1356 * functionality of the AutoMultiLockN class family (see the AutoMultiLock2
1357 * class for details) with two important differences:
1358 * <ol>
1359 * <li>Instances of AutoMultiWriteLockN classes are constructed from a list
1360 * of LockHandle or Lockable arguments directly instead of getting
1361 * intermediate LockOps interface pointers.
1362 * </li>
1363 * <li>All locks are requested in <b>write</b> mode.
1364 * </li>
1365 * <li>Since all locks are requested in write mode, bulk
1366 * AutoMultiWriteLockBase::leave() and AutoMultiWriteLockBase::enter()
1367 * operations are also available, that will leave and enter all managed
1368 * semaphores at once in the proper order (similarly to
1369 * AutoMultiWriteLockBase::lock() and AutoMultiWriteLockBase::unlock()).
1370 * </li>
1371 * </ol>
1372 *
1373 * Here is a typical usage pattern:
1374 * <code>
1375 * ...
1376 * LockHandle data1, data2;
1377 * ...
1378 * {
1379 * AutoMultiWriteLock2 multiLock (&data1, &data2);
1380 * // both locks are held in write mode here
1381 * }
1382 * // both locks are released here
1383 * </code>
1384 */
1385class AutoMultiWriteLock2 : public AutoMultiWriteLockBase <2>
1386{
1387public:
1388 AutoMultiWriteLock2 (A(0), A(1))
1389 { B(0); B(1); lock(); }
1390 AutoMultiWriteLock2 (C(0), C(1))
1391 { D(0); D(1); lock(); }
1392};
1393
1394/** AutoMultiWriteLock for 3 locks. See AutoMultiWriteLock2 for more details. */
1395class AutoMultiWriteLock3 : public AutoMultiWriteLockBase <3>
1396{
1397public:
1398 AutoMultiWriteLock3 (A(0), A(1), A(2))
1399 { B(0); B(1); B(2); lock(); }
1400 AutoMultiWriteLock3 (C(0), C(1), C(2))
1401 { D(0); D(1); D(2); lock(); }
1402};
1403
1404/** AutoMultiWriteLock for 4 locks. See AutoMultiWriteLock2 for more details. */
1405class AutoMultiWriteLock4 : public AutoMultiWriteLockBase <4>
1406{
1407public:
1408 AutoMultiWriteLock4 (A(0), A(1), A(2), A(3))
1409 { B(0); B(1); B(2); B(3); lock(); }
1410 AutoMultiWriteLock4 (C(0), C(1), C(2), C(3))
1411 { D(0); D(1); D(2); D(3); lock(); }
1412};
1413
1414#undef D
1415#undef C
1416#undef B
1417#undef A
1418
1419} /* namespace util */
1420
1421#endif // ____H_AUTOLOCK
1422
1423/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use