Recursive Upgradable Read/Write Lock for Windows

Environment: VC6, Windows NT/2000; Should work on 98/Me, but not tested there

Read/write lock is a synchronization mechanism that protects a shared resource in multithreaded environment. Standard mutex (and critical section) object provided by Windows allows only one thread a time to access the resource. In many cases this is overkill. Let's assume we have two or more reader threads that read contents of the resource without changing it, and one or more writer threads that can both read and write to the resource. In this case we can relax rules of protection a little bit:

Many operating systems implement this kind of protection natively. Unfortunately, no native synchronization object provided by Windows offers such a functionality. Nevertheless, read/write locks can be built in Windows based on available standard synchronization objects.

This article offers two lock objects:

They have almost identical interface, but slightly different implementation.

KReadWriteLock Class

This class is a recursive, but not upgradable read/write lock.
class KReadWriteLock
{
	KReadWriteLock( KReadWriteLock const& ); // not implemented
	void operator=( KReadWriteLock const& ); // not implemented

public:
	KReadWriteLock();
	
	bool LockRead( DWORD Timeout = INFINITE );
	bool LockWrite( DWORD Timeout = INFINITE );
	bool UnlockRead( DWORD Timeout = INFINITE );
	bool UnlockWrite( DWORD Timeout = INFINITE );
};

Locking rules for KReadWriteLock are as follows:

KReadWriteLock prefers neither writers nor readers. I.e. if both writer(s) and reader(s) are waiting for the lock, any of them can grab the lock when it is released by previous owner.

KReadWriteLock is recursive, but not upgradable. This means that

KReadWriteLockEx Class

This class is a recursive upgradable read/write lock that prefers writers over readers.
class KReadWriteLockEx
{
	KReadWriteLockEx( KReadWriteLockEx const& ); // not implemented
	void operator=( KReadWriteLockEx const& ); // not implemented

public:
	KReadWriteLockEx();
	
	bool LockRead( DWORD Timeout = INFINITE );
	bool LockWrite( DWORD Timeout = INFINITE );
	bool UnlockRead( DWORD Timeout = INFINITE );
	bool UnlockWrite( DWORD Timeout = INFINITE );
};

KReadWriteLockEx offers more flexibility: it is both recursive and upgradable. That is, if you have a read lock, you can safely acquire write lock and vice versa. Further more, you can even overlap locks:

KReadWriteLockEx Lock;
Lock.LockRead();
Lock.LockWrite();
Lock.UnlockRead();
Lock.UnlockWrite();

KReadWriteLockEx prefers writers over readers. That is, if a writer is waiting on the lock, no new readers are allowed to access the resource. Existing readers can continue to use the resource until they release the lock. This prevents so-called "writer starvation".

For all this flexibility, KReadWriteLockEx is slower than KreadWriteLock, takes more memory and its implementation is more complex. This is because KReadWriteLockEx must manually keep track of what threads are currently owning the lock. In case of KReadWriteLock this job is done by the operating system.

NOTE: neither KReadWriteLock nor KReadWriteLockEx will work between processes. Although, they can be extended to do so.

Helper Classes

Helper classes KReadLock, KWriteLock, KReadLockEx, and KWriteLockEx are used for automatic acquisition and release of locks. They are similar in nature to CSingleLock class from MFC. Their interfaces are almost identical and look like this:
class KReadLock
{
    KReadLock(KReadWriteLock& Lock, bool bInitialLock = true);
    ~KReadLockTempl();

    bool Lock(DWORD Timeout = INFINITE);
    bool Unlock(DWORD Timeout = INFINITE);
};
Use of methods is pretty straightforward and similar to CSingleLock. Note, that unlike CSingleLock, helper classes will acquire the lock in constructor by default.

Downloads

Download demo project - 26 Kb
Another demo - using helper classes - 23 Kb
Download source - 4 Kb