VirtualBox

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

Last change on this file since 8083 was 8083, checked in by vboxsync, 17 years ago

Main: Renamed AutoLock => AutoWriteLock; AutoReaderLock => AutoReadLock.

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