Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++/CLI

Ultra-simple C++ Read/Write Lock Class for Windows

3.43/5 (11 votes)
16 Nov 2006CPOL1 min read 1   1.4K  
Ultra-simple read/write lock pattern implementation in C++.

ReadWriteLock Test in action

Introduction

The CReadWriteLock class is a very simple class that implements the Reader/Writer pattern, used for synchronizing multiple thread access to a shared resource. By allowing either multiple reader access or single write access, this class provides a better throughput than standard synchronization mechanisms like Critical Sections.

Background

Looking for a simple solution on the net, I faced the fact that the solutions often were advanced, and not developed in the spirit of KISS (Keep It Simple S*****). Therefore, I decided to implement a simple yet functional solution.

Please note that in order to prevent reader starvation, the CReadWriteLock should only be used when reader access is far more common than writer access. The reason for this is that the implementation does not contain any thread queue or similar to secure the access order, and writer threads will always win this race.

Using the code

Just copy the class (ReadWriteLock.cpp and ReadWriteLock.h) to the project folder, and add the two files to the project.

The code is pretty much self-explanatory; create an instance of CReadWriteLock, and use respectively LockReader()/UnlockReader() and LockWriter()/UnlockWriter() before using the shared resource.

ReadWriteLock.h

C++
##pragma once
#include "Windows.h"

class CReadWriteLock
{
public:
    //! Constructor.

    CReadWriteLock(void);
    //! Destructor.

    virtual ~CReadWriteLock(void);
    
    //! Lock for reader access.

    void LockReader();
    //! Unlock reader access.

    void UnlockReader();
    
    //! Lock for writer access.

    void LockWriter();
    //! Unlock writer access.

    void UnlockWriter();

private:
    //! Private copy constructor.

    CReadWriteLock(const CReadWriteLock &cReadWriteLock);
    //! Private assignment operator.

    const CReadWriteLock& operator=(const CReadWriteLock &cReadWriteLock);

    //! Increment number of readers.

    void IncrementReaderCount();
    //! Decrement number of readers.

    void DecrementReaderCount();

    //! Writer access event.

    HANDLE m_hWriterEvent;
    //! No readers event.

    HANDLE m_hNoReadersEvent;
    //! Number of readers.

    int m_nReaderCount;
    
    //! Critical section for protecting lock writer method.

    CRITICAL_SECTION m_csLockWriter;
    //! Critical section for protecting reader count.

    CRITICAL_SECTION m_csReaderCount;
};

ReadWriteLock.cpp

C++
#include "StdAfx.h"

#include "ReadWriteLock.h"


CReadWriteLock::CReadWriteLock(void)
 : m_nReaderCount(0), m_hWriterEvent(NULL), m_hNoReadersEvent(NULL)
{
    // Create writer event with manual reset and default signaled state.
    // 

    // State:

    //          Signaled     = Writer has currently not access.
    //          Non-signaled = Writer has currently access, block readers.
    //

    m_hWriterEvent    = CreateEvent(NULL, TRUE, TRUE, NULL);
    
    // Create no readers event with manual reset and default signaled state.
    // 

    // State:

    //          Signaled     = No readers have currently access.
    //          Non-signaled = Some readers have currently access, block writer.
    //

    m_hNoReadersEvent = CreateEvent(NULL, TRUE, TRUE, NULL);

    //
    // Initialize critical sections.

    InitializeCriticalSection(&m_csLockWriter);
    InitializeCriticalSection(&m_csReaderCount);
}

CReadWriteLock::CReadWriteLock(const CReadWriteLock &cReadWriteLock)
{
}

const CReadWriteLock& CReadWriteLock::operator=(const CReadWriteLock &cReadWriteLock)
{
    return *this;
}


CReadWriteLock::~CReadWriteLock(void)
{
    //
    // Delete critical sections.

    DeleteCriticalSection(&m_csLockWriter);
    DeleteCriticalSection(&m_csReaderCount);

    // Close the writer event.

    CloseHandle(m_hWriterEvent);
    // Close the no readers event.

    CloseHandle(m_hNoReadersEvent);
}


void CReadWriteLock::LockReader()
{
    bool bLoop = true;

    // Loop.

    while(bLoop)
    {
        // Wait for Writer event to be signaled.

        WaitForSingleObject(m_hWriterEvent, INFINITE);
        // Increment number of readers.

        IncrementReaderCount();
        // If writer is become non-signaled fall back (double locking).

        if(WaitForSingleObject(m_hWriterEvent, 0) != WAIT_OBJECT_0)
        {
            // Decrement number of readers.
            DecrementReaderCount();
        }
        else
        {
            // Breakout.

            bLoop = false;
        }
    }
}


void CReadWriteLock::UnlockReader()
{
    // Decrement number of readers.

    DecrementReaderCount();
}


void CReadWriteLock::LockWriter()
{
    // Enter critical section (prevent more than one writer).

    EnterCriticalSection(&m_csLockWriter);
    // Wait for current writer.

    WaitForSingleObject(m_hWriterEvent, INFINITE);
    // Set writer to non-signaled.

    ResetEvent(m_hWriterEvent);
    // Wait for current readers to finish.

    WaitForSingleObject(m_hNoReadersEvent, INFINITE); 
    // Leave critical section.

    LeaveCriticalSection(&m_csLockWriter);
}


void CReadWriteLock::UnlockWriter()
{
    // Set writer event to signaled.

    SetEvent(m_hWriterEvent);
}


void CReadWriteLock::IncrementReaderCount()
{
    // Enter critical section.

    EnterCriticalSection(&m_csReaderCount);
    // Increase reader count.

    m_nReaderCount++;
    // Reset the no readers event.

    ResetEvent(m_hNoReadersEvent);
    // Leave critical section.

    LeaveCriticalSection(&m_csReaderCount);
}


void CReadWriteLock::DecrementReaderCount()
{
    // Enter critical section.

    EnterCriticalSection(&m_csReaderCount);
    // Decrease reader count.

    m_nReaderCount--;
    // Are all readers out?

    if(m_nReaderCount <= 0)
    {
        // Set the no readers event.

        SetEvent(m_hNoReadersEvent);
    }
    // Leave critical section.

    LeaveCriticalSection(&m_csReaderCount);
}

History

  • 2006-11-01: Version 1.0 - First release.
  • 2006-11-20: Version 1.1 - Shallow copy is prevented by implementing private constructor and assignment operator.
  • 2006-11-21: Version 1.2 - while loop waiting for no readers in LockWriter() is replaced with WaitForSingleObject().

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)