
class CMutexRW
{
protected:
    HANDLE        m_semReaders;
    HANDLE        m_semWriters;
    int        m_nReaders;
public:
    CMutexRW() :
        m_semReaders(NULL),
        m_semWriters(NULL),
        m_nReaders(0)
    {
        // initialize the Readers & Writers variables
        m_semReaders    = ::CreateSemaphore(NULL, 1, 1, NULL);
        m_semWriters    = ::CreateSemaphore(NULL, 1, 1, NULL);
        m_nReaders    = 0;

        if (m_semReaders == NULL || m_semWriters == NULL)
        {
            LPVOID lpMsgBuf;
            FormatMessage( 
                FORMAT_MESSAGE_ALLOCATE_BUFFER | 
                FORMAT_MESSAGE_FROM_SYSTEM | 
                FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                GetLastError(),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPTSTR) &lpMsgBuf,
                0,
                NULL 
                );
            TRACE( "***** ERROR: CreateSemaphore: %s\n", (LPCTSTR)lpMsgBuf );
            LocalFree( lpMsgBuf );            
        }
    };

    virtual ~CMutexRW()
    {
        if (m_semWriters)
            VERIFY( ::CloseHandle(m_semWriters) );    

        m_semWriters = NULL;
        if (m_semReaders)
            VERIFY( ::CloseHandle(m_semReaders) );    
        m_semReaders = NULL;
    }

    inline void Lock_DataRead(){
        DWORD dwEvent = WAIT_TIMEOUT;

        // P( semReaders )
        dwEvent = ::WaitForSingleObject( m_semReaders, INFINITE );
        ASSERT(dwEvent == WAIT_OBJECT_0);

        m_nReaders++;

        if (m_nReaders == 1)
        {
            // P( semWriters )
            dwEvent = ::WaitForSingleObject( m_semWriters, INFINITE );
            ASSERT(dwEvent == WAIT_OBJECT_0);
        }
        // V( semReaders )
        VERIFY( ::ReleaseSemaphore( m_semReaders, 1, NULL ) );
    };

    inline void Unlock_DataRead(){
        DWORD dwEvent = WAIT_TIMEOUT;
        // P( semReaders )
        dwEvent = ::WaitForSingleObject( m_semReaders, INFINITE );
        ASSERT(dwEvent == WAIT_OBJECT_0);

        m_nReaders--;

        if (m_nReaders == 0)
        {
            // V( semWriters )
            VERIFY( ::ReleaseSemaphore(m_semWriters, 1, NULL) );
        }
        // V( semReaders )
        VERIFY( ::ReleaseSemaphore( m_semReaders, 1, NULL ) );
    };

    inline void Lock_DataWrite(){
        DWORD dwEvent = WAIT_TIMEOUT;

        // P( semWriters )
        dwEvent = ::WaitForSingleObject( m_semWriters, INFINITE );
        ASSERT(dwEvent == WAIT_OBJECT_0);
    }

    inline void Unlock_DataWrite(){
        // V( semWriters )
        VERIFY( ::ReleaseSemaphore(m_semWriters, 1, NULL) );
    };

};


class CReadLock
{
protected:
    CMutexRW* m_pMutexRW;
    bool      m_bIsLocked;
public:
    CReadLock(CMutexRW* pMutexRW, const bool bInitialLock = false) :
        m_pMutexRW(pMutexRW), m_bIsLocked(false)
    {
        ASSERT(m_pMutexRW);
        if (bInitialLock){
            m_pMutexRW->Lock_DataRead();
            m_bIsLocked = true;
        }
    };

    inline const bool& IsLocked() const{
        return m_bIsLocked;
    };