windows-nt/Source/XPSP1/NT/net/rras/mgm/sync.c
2020-09-26 16:20:57 +08:00

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();
}