Logo Search packages:      
Sourcecode: virtualbox-ose version File versions

util::AutoWriteLock Class Reference

#include <AutoLock.h>

List of all members.


Detailed Description

Provides safe management of read/write semaphores in write mode.

A read/write semaphore is represented by the LockHandle class. This semaphore can be requested ("locked") in two different modes: for reading and for writing. A write lock is exclusive and acts like a mutex: only one thread can acquire a write lock on the given semaphore at a time; all other threads trying to request a write lock or a read lock (see below) on the same semaphore will be indefinitely blocked until the owning thread releases the write lock.

A read lock is shared. This means that several threads can acquire a read lock on the same semaphore at the same time provided that there is no thread that holds a write lock on that semaphore. Note that when there are one or more threads holding read locks, a request for a write lock on another thread will be indefinitely blocked until all threads holding read locks release them.

Note that write locks can be nested -- the same thread can request a write lock on the same semaphore several times. In this case, the corresponding number of release calls must be done in order to completely release all nested write locks and make the semaphore available for locking by other threads.

Read locks can be nested too in which case the same rule of the equal number of the release calls applies. Read locks can be also nested into write locks which means that the same thread can successfully request a read lock if it already holds a write lock. However, please note that the opposite is not possible: if a thread tries to request a write lock on the same semaphore it is already holding a read lock, it will definitely produce a deadlock (i.e. it will block forever waiting for itself).

Note that instances of the AutoWriteLock class manage write locks of read/write semaphores only. In order to manage read locks, please use the AutoReadLock class.

Safe semaphore management consists of the following:

Note that the LockHandle class taken by AutoWriteLock constructors is an abstract base of the read/write semaphore. You should choose one of the existing subclasses of this abstract class or create your own subclass that implements necessary read and write lock semantics. The most suitable choice is the RWLockHandle class which provides full support for both read and write locks as describerd above. Alternatively, you can use the WriteLockHandle class if you only need write (exclusive) locking (WriteLockHandle requires less system resources and works faster).

A typical usage pattern of the AutoWriteLock class is as follows: struct Struct : public RWLockHandle { ... };

void foo (Struct &aStruct) { { // acquire a write lock of aStruct AutoWriteLock alock (aStruct);

// now we can modify aStruct in a thread-safe manner aStruct.foo = ...;

// note that the write lock will be automatically released upon // execution of the return statement below if (!aStruct.bar) return;

... }

// note that the write lock is automatically released here }

Locking policy

When there are multiple threads and multiple objects to lock, there is always a potential possibility to produce a deadlock if the lock order is mixed up. Here is a classical example of a deadlock when two threads need to lock the same two objects in a row but do it in different order: Thread 1: #1: AutoWriteLock (mFoo); ... #2: AutoWriteLock (mBar); ... Thread 2: #3: AutoWriteLock (mBar); ... #4: AutoWriteLock (mFoo); ...

If the threads happen to be scheduled so that #3 completes after #1 has completed but before #2 got control, the threads will hit a deadlock: Thread 2 will be holding mBar and waiting for mFoo at #4 forever because Thread 1 is holding mFoo and won't release it until it acquires mBar at #2 that will never happen because mBar is held by Thread 2.

One of ways to avoid the described behavior is to never lock more than one obhect in a row. While it is definitely a good and safe practice, it's not always possible: the application logic may require several simultaneous locks in order to provide data integrity.

One of the possibilities to solve the deadlock problem is to make sure that the locking order is always the same across the application. In the above example, it would mean that both threads should first requiest a lock of mFoo and then mBar (or vice versa). One of the methods to guarantee the locking order consistent is to introduce a set of locking rules. The advantage of this method is that it doesn't require any special semaphore implementation or additional control structures. The disadvantage is that it's the programmer who must make sure these rules are obeyed across the whole application so the human factor applies. Taking the simplicity of this method into account, it is chosen to solve potential deadlock problems when using AutoWriteLock and AutoReadLock classes. Here are the locking rules that must be obeyed by all users of these classes. Note that if more than one rule matches the given group of objects to lock, all of these rules must be met:

  1. If there is a parent-child (or master-slave) relationship between the locked objects, parent (master) objects must be locked before child (slave) objects.
  2. When a group of equal objects (in terms of parent-child or master-slave relationsip) needs to be locked in a raw, the lock order must match the sort order (which must be consistent for the given group).
Note that if there is no pragrammatically expressed sort order (e.g. the objects are not part of the sorted vector or list but instead are separate data members of a class), object class names sorted in alphabetical order must be used to determine the lock order. If there is more than one object of the given class, the object variable names' alphabetical order must be used as a lock order. When objects are not represented as individual variables, as in case of unsorted arrays/lists, the list of alphabetically sorted object UUIDs must be used to determine the sort order.

All non-standard locking order must be avoided by all means, but when absolutely necessary, it must be clearly documented at relevant places so it is well seen by other developers. For example, if a set of instances of some class needs to be locked but these instances are not part of the sorted list and don't have UUIDs, then the class description must state what to use to determine the lock order (maybe some property that returns an unique value per every object).

Definition at line 505 of file AutoLock.h.


Public Member Functions

void attach (const Lockable *aLockable)
void attach (const Lockable &aLockable)
void attach (LockHandle &aHandle)
void attach (LockHandle *aHandle)
 AutoWriteLock (const Lockable *aLockable)
 AutoWriteLock (const Lockable &aLockable)
 AutoWriteLock (LockHandle &aHandle)
 AutoWriteLock (LockHandle *aHandle)
 AutoWriteLock ()
bool belongsTo (const Lockable *aLockable)
bool belongsTo (const Lockable &aLockable)
bool belongsTo (const LockHandle *aHandle) const
bool belongsTo (const LockHandle &aHandle) const
void detach ()
void enter ()
bool isNull () const
bool isWriteLockOnCurrentThread () const
void leave ()
void lock ()
void maybeEnter ()
void maybeLeave ()
bool operator! () const
void unlock ()
uint32_t writeLockLevel () const
 ~AutoWriteLock ()

Private Attributes

uint32_t mGlobalLockLevel
LockHandlemHandle
uint32_t mLockLevel

Friends

class AutoMultiWriteLockBase

The documentation for this class was generated from the following file:

Generated by  Doxygen 1.6.0   Back to index