/*-------------------------------------------------------------------------
* src\fedsrv\SWMRG.CPP
*
* Single Writer, Multiple Reader w/ Guard
*
* Owner:
*
* Copyright 1986-1998 Microsoft Corporation, All Rights Reserved
*-----------------------------------------------------------------------*/
/*
#include <windows.h>
#include <string.h>
#include <tchar.h>
#include "SWMRG.H" // The header file
*/
#include "pch.h"
/*-------------------------------------------------------------------------
* ConstructObjName
*-------------------------------------------------------------------------
* Purpose:
* Just construct a name for the kernel object
*
* Returns:
* the munged name
*/
static LPCTSTR ConstructObjName (LPCTSTR lpszPrefix, LPCTSTR lpszSuffix,
LPTSTR lpszFullName, size_t cbFullName, bool * pfOk)
{
*pfOk = true; // Assume success.
if (lpszSuffix == NULL)
return(NULL);
if ((_tcslen(lpszPrefix) + _tcslen(lpszSuffix)) >= cbFullName)
{
// If the strings will overflow the buffer,
// indicate an error.
*pfOk = false;
return NULL;
}
_tcscpy(lpszFullName, lpszPrefix);
_tcscat(lpszFullName, lpszSuffix);
return(lpszFullName);
}
/*-------------------------------------------------------------------------
* FSWMRGInitialize
*-------------------------------------------------------------------------
* Purpose:
* Initializes a SWMRG structure. This structure must be
* initialized before any writer or reader threads attempt
* to wait on it.
* The structure must be allocated by the application and
* the structure's address is passed as the first parameter.
* The lpszName parameter is the name of the object. Pass
* NULL if you do not want to share the object.
*
* Returns:
* success
*/
bool FSWMRGInitialize(PSWMRG pSWMRG, LPCTSTR lpszName)
{
TCHAR szFullObjName[100];
LPCTSTR lpszObjName;
bool fOk;
// Initialize all data members to NULL so that we can
// accurately check whether an error has occured.
pSWMRG->hMutexNoWriter = NULL;
pSWMRG->hEventNoReaders = NULL;
pSWMRG->hSemNumReaders = NULL;
// This mutex guards access to the other objects
// managed by this data structure and also indicates
// whether there any writer threads are writing.
// Initially no thread owns the mutex.
lpszObjName = ConstructObjName(__TEXT("SWMRGMutexNoWriter"), lpszName,
szFullObjName, ARRAY_SIZE(szFullObjName), &fOk);
if (fOk)
pSWMRG->hMutexNoWriter = CreateMutex(NULL, FALSE, lpszObjName);
// Create the manual-reset event that is signalled when
// no reader threads are reading.
// Initially no reader threads are reading.
lpszObjName = ConstructObjName(__TEXT("SWMRGEventNoReaders"), lpszName,
szFullObjName, ARRAY_SIZE(szFullObjName), &fOk);
if (fOk)
pSWMRG->hEventNoReaders = CreateEvent(NULL, TRUE, TRUE, lpszObjName);
// Initialize the variable that indicates the number of
// reader threads that are reading.
// Initially no reader threads are reading.
lpszObjName = ConstructObjName(__TEXT("SWMRGSemNumReaders"), lpszName,
szFullObjName, ARRAY_SIZE(szFullObjName), &fOk);
if (fOk)
pSWMRG->hSemNumReaders = CreateSemaphore(NULL, 0, 0x7FFFFFFF, lpszObjName);
if (NULL == pSWMRG->hMutexNoWriter ||
NULL == pSWMRG->hEventNoReaders ||
NULL == pSWMRG->hSemNumReaders)
{
// If a synchronization object could not be created,
// destroy any created objects and return failure.
SWMRGDelete(pSWMRG);
fOk = false;
}
else
{
fOk = true;
}
// Return TRUE upon success, FALSE upon failure.
return(fOk);
}
/*-------------------------------------------------------------------------
* SWMRGDelete
*-------------------------------------------------------------------------
* Purpose:
* Deletes the system resources associated with a SWMRG
* structure. The structure must be deleted only when
* no writer or reader threads in the calling process
* will wait on it.
*/
void SWMRGDelete(PSWMRG pSWMRG)
{
// Destroy any synchronization objects that were
// successfully created.
if (pSWMRG->hMutexNoWriter)
CloseHandle(pSWMRG->hMutexNoWriter);
if (pSWMRG->hEventNoReaders)
CloseHandle(pSWMRG->hEventNoReaders);
if (pSWMRG->hSemNumReaders)
CloseHandle(pSWMRG->hSemNumReaders);
}
/*-------------------------------------------------------------------------
* SWMRGWaitToWrite
*-------------------------------------------------------------------------
* Purpose:
* A writer thread calls this function to know when
* it can successfully write to the shared data.
*
* Returns:
* WaitForMultipleObjects()
*/
DWORD SWMRGWaitToWrite(PSWMRG pSWMRG, DWORD dwTimeout)
{
DWORD dw;
HANDLE rgHandles[2];
// We can write if the following are true:
// 1. The mutex guard is available and
// no other threads are writing.
// 2. No threads are reading.
rgHandles[0] = pSWMRG->hMutexNoWriter;
rgHandles[1] = pSWMRG->hEventNoReaders;
dw = WaitForMultipleObjects(2, rgHandles, TRUE, dwTimeout);
if (dw != WAIT_TIMEOUT)
{
// This thread can write to the shared data.
// Because a writer thread is writing, the mutex should not
// not be released. This stops other writers and readers.
}
return(dw);
}
/*-------------------------------------------------------------------------
* SWMRGDoneWriting
*-------------------------------------------------------------------------
* Purpose:
* A writer thread calls this function to let other threads
* know that it no longer needs to write to the shared data.
*/
void SWMRGDoneWriting(PSWMRG pSWMRG)
{
// Presumably, a writer thread calling this function has
// successfully called WaitToWrite. This means that we
// do not have to wait on any synchronization objects
// here because the writer already owns the mutex.
// Allow other writer/reader threads to use
// the SWMRG synchronization object.
ReleaseMutex(pSWMRG->hMutexNoWriter);
}
/*-------------------------------------------------------------------------
* SWMRGWaitToRead
*-------------------------------------------------------------------------
* Purpose:
* A reader thread calls this function to know when
* it can successfully read the shared data.
*
* Returns:
* WaitForSingleObject()
*/
DWORD SWMRGWaitToRead(PSWMRG pSWMRG, DWORD dwTimeout)
{
DWORD dw;
LONG lPreviousCount;
// We can read if the mutex guard is available
// and no threads are writing.
dw = WaitForSingleObject(pSWMRG->hMutexNoWriter, dwTimeout);
if (dw != WAIT_TIMEOUT)
{
// This thread can read from the shared data.
// Increment the number of reader threads.
ReleaseSemaphore(pSWMRG->hSemNumReaders, 1, &lPreviousCount);
if (lPreviousCount == 0)
{
// If this is the first reader thread,
// set our event to reflect this.
ResetEvent(pSWMRG->hEventNoReaders);
}
// Allow other writer/reader threads to use
// the SWMRG synchronization object.
ReleaseMutex(pSWMRG->hMutexNoWriter);
}
return(dw);
}
/*-------------------------------------------------------------------------
* SWMRGDoneReading
*-------------------------------------------------------------------------
* Purpose:
* A reader thread calls this function to let other threads
* know when it no longer needs to read the shared data.
*/
void SWMRGDoneReading(PSWMRG pSWMRG)
{
bool fLastReader;
HANDLE rgHandles[2];
// We can stop reading if the mutex guard is available,
// but when we stop reading we must also decrement the
// number of reader threads.
rgHandles[0] = pSWMRG->hMutexNoWriter;
rgHandles[1] = pSWMRG->hSemNumReaders;
WaitForMultipleObjects(2, rgHandles, TRUE, INFINITE);
fLastReader = WAIT_TIMEOUT == WaitForSingleObject(pSWMRG->hSemNumReaders, 0);
if (fLastReader)
{
// If this is the last reader thread,
// set our event to reflect this.
SetEvent(pSWMRG->hEventNoReaders);
}
else
{
// If this is NOT the last reader thread, we successfully
// waited on the semaphore. We must release the semaphore
// so that the count accurately reflects the number
// of reader threads.
ReleaseSemaphore(pSWMRG->hSemNumReaders, 1, NULL);
}
// Allow other writer/reader threads to use
// the SWMRG synchronization object.
ReleaseMutex(pSWMRG->hMutexNoWriter);
}