655 lines
15 KiB
C
655 lines
15 KiB
C
|
//============================================================================
|
||
|
// 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();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|