/*++

Copyright (c) 2000, Microsoft Corporation

Module Name:


Abstract:

    The file contains the READ_WRITE_LOCK definition which allows
    multiple-reader/single-writer.  This implementation DOES NOT
    starve a thread trying to acquire write accesss if there are
    a large number of threads interested in acquiring read access.

Revision History:

    mohitt, sachins, Apr 23 2000, Created

--*/

#include "pcheapol.h"
#pragma hdrstop

//----------------------------------------------------------------------------
// Function: CreateReadWriteLock
//
// Initializes a multiple-reader/single-writer lock object
//----------------------------------------------------------------------------

DWORD
CreateReadWriteLock(
    PREAD_WRITE_LOCK    pRWL,
    PCHAR               szName
    ) 
{

    sprintf(pRWL->RWL_Name, "%.3s", szName);
    
    pRWL->RWL_ReaderCount = 0;

    __try {
        InitializeCriticalSection(&(pRWL)->RWL_ReadWriteBlock);
    }
    __except(EXCEPTION_EXECUTE_HANDLER) {
        return GetLastError();
    }

    pRWL->RWL_ReaderDoneEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
    if (pRWL->RWL_ReaderDoneEvent == NULL) {
        return GetLastError();
    }

    return NO_ERROR;
}



//----------------------------------------------------------------------------
// Function:    DeleteReadWriteLock
//
// Frees resources used by a multiple-reader/single-writer lock object
//----------------------------------------------------------------------------

VOID
DeleteReadWriteLock(
    PREAD_WRITE_LOCK pRWL
    ) 
{

    CloseHandle(pRWL->RWL_ReaderDoneEvent);
    pRWL->RWL_ReaderDoneEvent = NULL;
    DeleteCriticalSection(&pRWL->RWL_ReadWriteBlock);
    pRWL->RWL_ReaderCount = 0;
}



//----------------------------------------------------------------------------
// Function:    AcquireReadLock
//
// Secures shared ownership of the lock object for the caller.
//
// readers enter the read-write critical section, increment the count,
// and leave the critical section
//----------------------------------------------------------------------------

VOID
AcquireReadLock(
    PREAD_WRITE_LOCK pRWL
    ) 
{

    EnterCriticalSection(&pRWL->RWL_ReadWriteBlock); 
    InterlockedIncrement(&pRWL->RWL_ReaderCount);
    LeaveCriticalSection(&pRWL->RWL_ReadWriteBlock);
}



//----------------------------------------------------------------------------
// Function:    ReleaseReadLock
//
// Relinquishes shared ownership of the lock object.
//
// the last reader sets the event to wake any waiting writers
//----------------------------------------------------------------------------

VOID
ReleaseReadLock(
    PREAD_WRITE_LOCK pRWL
    ) 
{

    if (InterlockedDecrement(&pRWL->RWL_ReaderCount) < 0) {
        SetEvent(pRWL->RWL_ReaderDoneEvent); 
    }
}



//----------------------------------------------------------------------------
// Function:    AcquireWriteLock
//
// Secures exclusive ownership of the lock object.
//
// the writer blocks other threads by entering the ReadWriteBlock section,
// and then waits for any thread(s) owning the lock to finish
//----------------------------------------------------------------------------

VOID
AcquireWriteLock(
    PREAD_WRITE_LOCK pRWL
    ) 
{

    EnterCriticalSection(&pRWL->RWL_ReadWriteBlock);
    if (InterlockedDecrement(&pRWL->RWL_ReaderCount) >= 0) { 
        WaitForSingleObject(pRWL->RWL_ReaderDoneEvent, INFINITE);
    }
}



//----------------------------------------------------------------------------
// Function:    ReleaseWriteLock
//
// Relinquishes exclusive ownership of the lock object.
//
// the writer releases the lock by setting the count to zero
// and then leaving the ReadWriteBlock critical section
//----------------------------------------------------------------------------

VOID
ReleaseWriteLock(
    PREAD_WRITE_LOCK pRWL
    ) 
{

    InterlockedIncrement(&pRWL->RWL_ReaderCount);
    LeaveCriticalSection(&(pRWL)->RWL_ReadWriteBlock);
}

#if 0

//----------------------------------------------------------------------------
// InitializeDynamicLocksStore
//
// Initialize the structure from which dynamic readwrite locks are allocated.
//----------------------------------------------------------------------------

DWORD
InitializeDynamicLocksStore (
    PDYNAMIC_LOCKS_STORE    pStore,
    HANDLE                  hHeap,
    PCHAR                   szName
    ) 
{

    sprintf(pStore->szName, "%.3s", szName);
    
    // initialize the heap from where dynamic locks are allocated
    pStore->hHeap = hHeap;
    
    INITIALIZE_LOCKED_LIST(&pStore->llFreeLocksList, szName);
    if (!LOCKED_LIST_INITIALIZED(&pStore->llFreeLocksList))
        return GetLastError();

    // initialize the count of the number of free and allocated locks
    pStore->ulCountAllocated = pStore->ulCountFree = 0;

    return NO_ERROR;
}



//----------------------------------------------------------------------------
// DeInitializeDynamicLocksStore
//
// Fail if any allocated locks have not been freed.
// Delete the free locks and the FreeLocksList.
//----------------------------------------------------------------------------

DWORD
DeInitializeDynamicLocksStore (
    PDYNAMIC_LOCKS_STORE    pStore
    ) 
{
    
    PDYNAMIC_READWRITE_LOCK pLock;
    PLIST_ENTRY             pleHead, ple;

    // can't complete if any locks are still allocated!!!
    if (pStore->ulCountAllocated)
        return ERROR_CAN_NOT_COMPLETE;

    // deinitialize the count of the number of free and allocated locks
    pStore->ulCountAllocated = pStore->ulCountFree = 0;

    // deinitialize the FreeLocksList
    pStore->llFreeLocksList.created = 0;

    // delete all dynamic readwrite locks and free the memory.
    pleHead = &(pStore->llFreeLocksList.head);
    for (ple = pleHead->Flink; ple != pleHead; ple = ple->Flink)
    {
        pLock = CONTAINING_RECORD(ple, DYNAMIC_READWRITE_LOCK, leLink);
        DELETE_READ_WRITE_LOCK(&pLock->rwlLock);
        HeapFree(pStore->hHeap, 0, pLock);
    }

    DeleteCriticalSection(&(pStore->llFreeLocksList.lock));

    // deinitialize the heap from where dynamic locks are allocated
    pStore->hHeap = NULL;

    return NO_ERROR;
}



//----------------------------------------------------------------------------
// GetDynamicReadwriteLock
//
// Return a free dynamic readwrite lock, if one is available.
// Else allocate a new dynamic readwrite lock.
// Assumes pStore->llFreeLocksList is locked.
//----------------------------------------------------------------------------

PDYNAMIC_READWRITE_LOCK
GetDynamicReadwriteLock (
    PDYNAMIC_LOCKS_STORE    pStore
    ) 
{

    PDYNAMIC_READWRITE_LOCK pLock;
    PLIST_ENTRY             pleHead, ple;

    do                          // breakout loop
    {
        // a free dynamic lock is available. Return it
        pleHead = &(pStore->llFreeLocksList.head);
        if (!IsListEmpty(pleHead))
        {
            pStore->ulCountFree--;
            ple = RemoveHeadList(pleHead);
            pLock = CONTAINING_RECORD(ple, DYNAMIC_READWRITE_LOCK, leLink);
            break;
        }
    
        // allocate memory for a new dynamic lock
        pLock = HeapAlloc(pStore->hHeap, 0, sizeof(DYNAMIC_READWRITE_LOCK));
        if (pLock ==  NULL)
            break;

        // initialize the fields
        CREATE_READ_WRITE_LOCK(&(pLock->rwlLock), pStore->szName);
        if (!READ_WRITE_LOCK_CREATED(&(pLock->rwlLock)))
        {
            HeapFree(pStore->hHeap, 0, pLock);
            pLock = NULL;
            break;
        }
    } while (FALSE);

    if (pLock != NULL)
    {
        pStore->ulCountAllocated++;
        pLock->ulCount = 0;
    }

    return pLock;
}
    

    
//----------------------------------------------------------------------------
// FreeDynamicReadwriteLock
//
// Accepts a released dynamic readwrite lock.
// Frees it if there are too many dynamic readwrite locks.
// Assumes pStore->llFreeLocksList is locked.
//----------------------------------------------------------------------------

VOID
FreeDynamicReadwriteLock (
    PDYNAMIC_READWRITE_LOCK pLock,
    PDYNAMIC_LOCKS_STORE    pStore
    ) 
{

    PLIST_ENTRY             pleHead;


    // decrement count of allocated locks
    pStore->ulCountAllocated--;

    // if there are too many dynamic readwrite locks, then free this lock
    if ((pStore->ulCountAllocated + pStore->ulCountFree + 1) >
        DYNAMIC_LOCKS_HIGH_THRESHOLD)
    {
        DELETE_READ_WRITE_LOCK(&pLock->rwlLock);
        HeapFree(pStore->hHeap, 0, pLock);    
    }
    else                        // insert into the list of free locks
    {
        pleHead = &(pStore->llFreeLocksList.head);
        InsertHeadList(pleHead, &pLock->leLink);
        pStore->ulCountFree++;
    }

    return;
}



//----------------------------------------------------------------------------
// AcquireDynamicLock
//
// Locks the FreeLocksList.
// Allocates a new dynamic lock if required.
// Increments the count.
// Unlocks the FreeLocksList.
// Acquires the dynamic lock.
//----------------------------------------------------------------------------

DWORD
AcquireDynamicReadwriteLock (
    PDYNAMIC_READWRITE_LOCK *ppLock,
    LOCK_MODE               lmMode,
    PDYNAMIC_LOCKS_STORE    pStore
    ) 
{

    // acquire the lock for the free locks list
    AcquireListLock(&pStore->llFreeLocksList);
    
    // if it does not already exist, allocate a new dynamic lock
    if (*ppLock == NULL)
    {
        *ppLock = GetDynamicReadwriteLock(pStore);

        // if could not get a lock we are in serious trouble
        if (*ppLock == NULL)
        {
            ReleaseListLock(&pStore->llFreeLocksList);
            return ERROR_CAN_NOT_COMPLETE;
        }
    }

    // increment count in the dynamic lock
    (*ppLock)->ulCount++;

    // release the lock for the free locks list
    ReleaseListLock(&pStore->llFreeLocksList);    

    // acquire dynamic lock
    if (lmMode == READ_MODE)
        AcquireReadLock(&(*ppLock)->rwlLock);
    else
        AcquireWriteLock(&(*ppLock)->rwlLock);

    return NO_ERROR;
}



//----------------------------------------------------------------------------
// ReleaseDynamicReadwriteLock
//
// Locks the FreeLocksList.
// Releases the dynamic lock.
// Decrements the count.
// Free the dynamic lock if count becomes 0.
// Unlocks the FreeLocksList.
//----------------------------------------------------------------------------

VOID
ReleaseDynamicReadwriteLock (
    PDYNAMIC_READWRITE_LOCK *ppLock,
    LOCK_MODE               lmMode,
    PDYNAMIC_LOCKS_STORE    pStore
    ) 
{

    // acquire the lock for the free locks list
    AcquireListLock(&pStore->llFreeLocksList);

    // release the dynamic readwrite lock
    if (lmMode == READ_MODE)
        ReleaseReadLock(&(*ppLock)->rwlLock);
    else 
        ReleaseWriteLock(&(*ppLock)->rwlLock);

    // decrement count in the dynamic lock, free it if count becomes 0
    (*ppLock)->ulCount--;
    if ((*ppLock)->ulCount is 0)
    {
        FreeDynamicReadwriteLock(*ppLock, pStore);
        *ppLock = NULL;         // so it is known that it doesn't exist 
    }

    // release the lock for the free locks list
    ReleaseListLock(&pStore->llFreeLocksList);    

    return;
}
#endif