/*++ 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