/*------------------------------------------------------------------------- * src\fedsrv\SWMRG.CPP * * Single Writer, Multiple Reader w/ Guard * * Owner: * * Copyright 1986-1998 Microsoft Corporation, All Rights Reserved *-----------------------------------------------------------------------*/ /* #include #include #include #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); }