|
|
//============================================================================
// Copyright (c) 1995, Microsoft Corporation
//
// File: sync.c
//
// History:
// V Raman July-11-1997 Created.
//
// Basic locking operations. Borrowed from RIP implementation by abolade
//============================================================================
#include "pchmgm.h"
#pragma hdrstop
//----------------------------------------------------------------------------
// Function: QueueMgmWorker
//
// This function is called to queue a MGM function in a safe fashion;
// if cleanup is in progress or if MGM has stopped, this function
// discards the work-item.
//----------------------------------------------------------------------------
DWORD QueueMgmWorker( WORKERFUNCTION pFunction, PVOID pContext ) {
DWORD dwErr;
ENTER_GLOBAL_SECTION(); if (ig.imscStatus != IPMGM_STATUS_RUNNING) {
//
// cannot queue a work function when MGM has quit or is quitting
//
dwErr = ERROR_CAN_NOT_COMPLETE; } else {
++ig.lActivityCount;
dwErr = RtlQueueWorkItem(pFunction, pContext, 0);
if (dwErr != STATUS_SUCCESS) { --ig.lActivityCount; } }
LEAVE_GLOBAL_SECTION();
return dwErr; }
//----------------------------------------------------------------------------
// Function: EnterMgmAPI
//
// This function is called to when entering a MGM api, as well as
// when entering the input thread and timer thread.
// It checks to see if MGM has stopped, and if so it quits; otherwise
// it increments the count of active threads.
//----------------------------------------------------------------------------
BOOL EnterMgmAPI( ) {
BOOL bEntered;
ENTER_GLOBAL_SECTION();
if (ig.imscStatus == IPMGM_STATUS_RUNNING) {
//
// MGM is running, so the API may continue
//
++ig.lActivityCount;
bEntered = TRUE; } else {
//
// MGM is not running, so the API exits quietly
//
bEntered = FALSE; }
LEAVE_GLOBAL_SECTION();
return bEntered; }
//----------------------------------------------------------------------------
// Function: EnterMgmWorker
//
// This function is called when entering a MGM worker-function.
// Since there is a lapse between the time a worker-function is queued
// and the time the function is actually invoked by a worker thread,
// this function must check to see if MGM has stopped or is stopping;
// if this is the case, then it decrements the activity count,
// releases the activity semaphore, and quits.
//----------------------------------------------------------------------------
BOOL EnterMgmWorker( ) {
BOOL bEntered;
ENTER_GLOBAL_SECTION();
if (ig.imscStatus == IPMGM_STATUS_RUNNING) {
//
// MGM is running, so the function may continue
//
bEntered = TRUE; } else if (ig.imscStatus == IPMGM_STATUS_STOPPING) {
//
// MGM is not running, but it was, so the function must stop.
//
--ig.lActivityCount;
ReleaseSemaphore(ig.hActivitySemaphore, 1, NULL);
bEntered = FALSE; } else {
//
// MGM probably never started. quit quietly
//
bEntered = FALSE; }
LEAVE_GLOBAL_SECTION();
return bEntered; }
//----------------------------------------------------------------------------
// Function: LeaveMgmWorker
//
// This function is called when leaving a MGM API or worker function.
// It decrements the activity count, and if it detects that MGM has stopped
// or is stopping, it releases the activity semaphore.
//----------------------------------------------------------------------------
VOID LeaveMgmWorker( ) {
ENTER_GLOBAL_SECTION();
--ig.lActivityCount;
if (ig.imscStatus == IPMGM_STATUS_STOPPING) {
ReleaseSemaphore(ig.hActivitySemaphore, 1, NULL); }
LEAVE_GLOBAL_SECTION();
}
//----------------------------------------------------------------------------
// CreateReadWriteLock
//
// This function is called to create and initialize a new read-write lock
// structure. It is invoked by AcquireXLock ( X = { Read | Write } )
//----------------------------------------------------------------------------
DWORD CreateReadWriteLock( IN OUT PMGM_READ_WRITE_LOCK * ppmrwl ) {
DWORD dwErr; PMGM_READ_WRITE_LOCK pmrwl;
TRACELOCK1( "ENTERED CreateReadWriteLock : %x", ppmrwl );
do { *ppmrwl = NULL; //
// Allocate a lock structure
//
pmrwl = MGM_ALLOC( sizeof( MGM_READ_WRITE_LOCK ) );
if ( pmrwl == NULL ) { dwErr = GetLastError();
TRACE1( ANY, "CreateReadWriteLock failed to allocate lock : %x", dwErr );
break; }
//
// Init. critcal section
//
try { InitializeCriticalSection( &pmrwl-> csReaderWriterBlock ); } except ( EXCEPTION_EXECUTE_HANDLER ) { dwErr = GetLastError();
MGM_FREE( pmrwl );
TRACE1( ANY, "CreateReadWriteLock failed to initialize critical section : %x", dwErr );
break; }
//
// create reader done event.
//
pmrwl-> hReaderDoneEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
if ( pmrwl-> hReaderDoneEvent == NULL ) { dwErr = GetLastError(); MGM_FREE( pmrwl ); TRACE1( ANY, "CreateReadWriteLock failed to create event : %x", dwErr );
break; }
//
// initialize count fields
//
pmrwl-> lUseCount = 0;
pmrwl-> lReaderCount = 0;
pmrwl-> sleLockList.Next = NULL;
*ppmrwl = pmrwl;
dwErr = NO_ERROR; } while ( FALSE );
TRACELOCK1( "LEAVING CreateReadWriteLock : %x", dwErr );
return dwErr; }
//----------------------------------------------------------------------------
// DeleteReadWriteLock
//
// This functions destroys a read-write lock. It is invoked when MGM is
// being stopped. During normal operation when a read-write lock is no longer
// required it pushed onto a global stack of locks for reuse as needed.
//----------------------------------------------------------------------------
VOID DeleteReadWriteLock( IN PMGM_READ_WRITE_LOCK pmrwl ) { DeleteCriticalSection( &pmrwl-> csReaderWriterBlock );
CloseHandle( pmrwl-> hReaderDoneEvent );
MGM_FREE( pmrwl ); }
//----------------------------------------------------------------------------
// DeleteLockList
//
// This function deletes the entire list of locks present in the stack of
// read write locks. Assumes stack of locks is locked.
//----------------------------------------------------------------------------
VOID DeleteLockList( ) { PSINGLE_LIST_ENTRY psle = NULL;
PMGM_READ_WRITE_LOCK pmrwl = NULL;
TRACELOCK0( "ENTERED DeleteLockList" );
ENTER_GLOBAL_LOCK_LIST_SECTION();
psle = PopEntryList( &ig.llStackOfLocks.sleHead );
while ( psle != NULL ) { pmrwl = CONTAINING_RECORD( psle, MGM_READ_WRITE_LOCK, sleLockList );
DeleteReadWriteLock( pmrwl ); psle = PopEntryList( &ig.llStackOfLocks.sleHead ); }
LEAVE_GLOBAL_LOCK_LIST_SECTION();
TRACELOCK0( "LEAVING DeleteLockList"); }
//----------------------------------------------------------------------------
// AcquireReadLock
//
// This function provides read access to a protected resource. If needed
// it will reuse a lock from the stack of locks if available or allocate a
// new read-write lock.
//----------------------------------------------------------------------------
DWORD AcquireReadLock( IN OUT PMGM_READ_WRITE_LOCK * ppmrwl ) { DWORD dwErr = NO_ERROR; PSINGLE_LIST_ENTRY psle = NULL;
//
// determine if a lock needs to be allocated first.
// Perform this check with in a critical section so
// that two locks are not concurrently
// assigned to a single resource.
//
ENTER_GLOBAL_LOCK_LIST_SECTION();
if ( *ppmrwl == NULL ) { //
// get a lock from the stack of locks
//
psle = PopEntryList( &ig.llStackOfLocks.sleHead );
if ( psle != NULL ) { *ppmrwl = CONTAINING_RECORD( psle, MGM_READ_WRITE_LOCK, sleLockList ); }
else { //
// Stack of locks was empty. Create a new lock
//
dwErr = CreateReadWriteLock( ppmrwl );
if ( dwErr != NO_ERROR ) { //
// failed to create a lock. Possibly ran out of resources
//
LEAVE_GLOBAL_LOCK_LIST_SECTION(); TRACE2( ANY, "LEAVING AcquireReadLock, lock %x, error %x", ppmrwl, dwErr ); return dwErr; } } }
//
// *ppmrwl points to a valid lock structure.
//
InterlockedIncrement( &( (*ppmrwl)-> lUseCount ) );
TRACECOUNT1( "AcquireReadLock, Users %d", (*ppmrwl)-> lUseCount );
LEAVE_GLOBAL_LOCK_LIST_SECTION();
//
// Increment reader count
//
EnterCriticalSection( &( (*ppmrwl)-> csReaderWriterBlock ) );
InterlockedIncrement( &( (*ppmrwl)-> lReaderCount ) );
TRACECOUNT1( "Readers %d", (*ppmrwl)-> lReaderCount ); LeaveCriticalSection( &( (*ppmrwl)-> csReaderWriterBlock ) );
return NO_ERROR; }
//----------------------------------------------------------------------------
// ReleaseReadLock
//
// This function is invoked to release read access to a protected resource.
// If there are no more reader/writers waiting on this lock, the read-write
// lock is released to the global stack of locks for later reuse.
//----------------------------------------------------------------------------
VOID ReleaseReadLock( IN OUT PMGM_READ_WRITE_LOCK * ppmrwl ) { //
// Decrement reader count, and signal any waiting writer
//
if ( InterlockedDecrement( &( (*ppmrwl)-> lReaderCount ) ) < 0 ) { SetEvent( (*ppmrwl)-> hReaderDoneEvent ); }
//
// determine if the lock is being used. If not the lock should be
// released to the stack of locks.
//
ENTER_GLOBAL_LOCK_LIST_SECTION();
if ( InterlockedDecrement( &( (*ppmrwl)-> lUseCount ) ) == 0 ) { PushEntryList( &ig.llStackOfLocks.sleHead, &( (*ppmrwl)-> sleLockList ) ); *ppmrwl = NULL;
TRACECOUNT0( "ReleaseReadLock no more users" ); }
else { TRACECOUNT2( "ReleaseReadLock, Readers %x, users %x", (*ppmrwl)-> lReaderCount, (*ppmrwl)-> lUseCount ); } LEAVE_GLOBAL_LOCK_LIST_SECTION(); }
//----------------------------------------------------------------------------
// AcquireWriteLock
//
// This function provides write access to a protected resource. If needed
// it will reuse a lock from the stack of locks if available or allocate a
// new read-write lock.
//----------------------------------------------------------------------------
DWORD AcquireWriteLock( IN OUT PMGM_READ_WRITE_LOCK * ppmrwl ) { DWORD dwErr = NO_ERROR; PSINGLE_LIST_ENTRY psle = NULL; //
// determine is the you need to allocate a lock first. If needed
// do so under mutual exclusive so that two locks are not
// concurrently assigned for the same resources.
//
ENTER_GLOBAL_LOCK_LIST_SECTION();
if ( *ppmrwl == NULL ) { //
// get a lock from the stack of locks
//
psle = PopEntryList( &ig.llStackOfLocks.sleHead );
if ( psle != NULL ) { *ppmrwl = CONTAINING_RECORD( psle, MGM_READ_WRITE_LOCK, sleLockList ); }
else { //
// Stack of locks was empty. Create a new lock
//
dwErr = CreateReadWriteLock( ppmrwl );
if ( dwErr != NO_ERROR ) { LEAVE_GLOBAL_LOCK_LIST_SECTION(); return dwErr; } } }
//
// *ppmrwl points to a valid lock structure.
//
InterlockedIncrement( &( (*ppmrwl)-> lUseCount ) );
TRACECOUNT1( "AcquireWriteLock, Users %d", (*ppmrwl)-> lUseCount ); LEAVE_GLOBAL_LOCK_LIST_SECTION();
//
// acquire write lock.
//
EnterCriticalSection( &( (*ppmrwl)-> csReaderWriterBlock ) );
if ( InterlockedDecrement( &( (*ppmrwl)-> lReaderCount ) ) >= 0 ) { //
// other readers present. Wait for them to finish
//
TRACECOUNT1( "AcquireWriteLock, Readers %d", (*ppmrwl)-> lReaderCount ); WaitForSingleObject( (*ppmrwl)-> hReaderDoneEvent, INFINITE ); }
return dwErr; }
//----------------------------------------------------------------------------
// ReleaseWriteLock
//
// This function is invoked to release write access to a protected resource.
// If there are no more reader/writers waiting on this lock, the read-write
// lock is released to the global stack of locks for later reuse.
//----------------------------------------------------------------------------
VOID ReleaseWriteLock( IN OUT PMGM_READ_WRITE_LOCK * ppmrwl ) { //
// release the write lock
//
(*ppmrwl)-> lReaderCount = 0;
LeaveCriticalSection( &( (*ppmrwl)-> csReaderWriterBlock ) );
//
// determine if the lock is being used by anyone else.
// if not we need to release the lock back to the stack.
//
ENTER_GLOBAL_LOCK_LIST_SECTION();
if ( InterlockedDecrement( &( (*ppmrwl)-> lUseCount ) ) == 0 ) { PushEntryList( &ig.llStackOfLocks.sleHead, &( (*ppmrwl)-> sleLockList ) ); *ppmrwl = NULL; TRACECOUNT0( "ReleaseWriteLock no more users" ); }
else { TRACECOUNT2( "ReleaseWriteLock, Readers %x, users %x", (*ppmrwl)-> lReaderCount, (*ppmrwl)-> lUseCount ); } LEAVE_GLOBAL_LOCK_LIST_SECTION(); }
|