/* * This code implements synchronization objects code between threads. If you want, you can include it to your * code. This file is not dependent on any other external code (functions) * * (c) majvan 2002-2004 */ #include "stdafx.h" ///////////////////////////////////////////////////////////////////////////////////////// // 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. SWMRG::SWMRG(wchar_t *Name) { // Creates the automatic-reset event that is signalled when // no writer threads are writing. // Initially no reader threads are reading. if (Name != nullptr) Name[0] = (wchar_t)'W'; hEventNoWriter = ::CreateEvent(nullptr, FALSE, TRUE, Name); // Creates the manual-reset event that is signalled when // no reader threads are reading. // Initially no reader threads are reading. if (Name != nullptr) Name[0] = (wchar_t)'R'; hEventNoReaders = ::CreateEvent(nullptr, TRUE, TRUE, Name); // Initializes the variable that indicates the number of // reader threads that are reading. // Initially no reader threads are reading. if (Name != nullptr) Name[0] = (wchar_t)'C'; hSemNumReaders = ::CreateSemaphore(nullptr, 0, 0x7FFFFFFF, Name); if (Name != nullptr) Name[0] = (wchar_t)'F'; hFinishEV = ::CreateEvent(nullptr, TRUE, FALSE, Name); } // Destroys any synchronization objects that were // successfully created. SWMRG::~SWMRG() { if (nullptr != hEventNoWriter) CloseHandle(hEventNoWriter); if (nullptr != hEventNoReaders) CloseHandle(hEventNoReaders); if (nullptr != hSemNumReaders) CloseHandle(hSemNumReaders); if (nullptr != hFinishEV) CloseHandle(hFinishEV); } ///////////////////////////////////////////////////////////////////////////////////////// // SWMRGWaitToWrite // A writer thread calls this function to know when // it can successfully write to the shared data. // returns WAIT_FINISH when we are in write-access or WAIT_FAILED // when event about quick finishing is set (or when system returns fail when waiting for synchro object) uint32_t SWMRG::WaitToWrite(uint32_t dwTimeout) { uint32_t dw; HANDLE aHandles[2]; // Netlib_Logf(hNetlibUser, "SWMRGWaitToWrite %p", this); // We can write if the following are true: // 1. No other threads are writing. // 2. No threads are reading. // But first we have to know if SWMRG structure is not about to delete aHandles[0] = hEventNoWriter; aHandles[1] = hEventNoReaders; if (WAIT_OBJECT_0 == (dw = WaitForSingleObject(hFinishEV, 0))) return WAIT_FINISH; if (WAIT_FAILED == dw) return dw; dw = WaitForMultipleObjects(2, aHandles, TRUE, dwTimeout); // if a request to delete became later, we should not catch it. Try once more to ask if account is not about to delete if ((dw != WAIT_FAILED) && (WAIT_OBJECT_0 == (WaitForSingleObject(hFinishEV, 0)))) { SetEvent(hEventNoWriter); return WAIT_FINISH; } // This thread can write to the shared data. // Automatic event for NoWriter sets hEventNoWriter to nonsignaled after WaitForMultipleObject // Because a writer thread is writing, the Event // should not be reset. This stops other // writers and readers. return dw; } ///////////////////////////////////////////////////////////////////////////////////////// // SWMRGDoneWriting // A writer thread calls this function to let other threads // know that it no longer needs to write to the shared data. void SWMRG::DoneWriting() { // Netlib_Logf(hNetlibUser, "SWMRGDoneWriting %p", this); // Allow other writer/reader threads to use // the SWMRG synchronization object. SetEvent(hEventNoWriter); } ///////////////////////////////////////////////////////////////////////////////////////// // SWMRGWaitToRead // is used to wait for read access with SWMRG SO, but it also increments counter if successfull // returns WAIT_FAILED or WAIT_FINISH // when WAIT_FAILED, we should not begin to access datas, we are not in read-access mode uint32_t SWMRG::WaitToRead(uint32_t dwTimeout) { uint32_t dw; LONG lPreviousCount; // Netlib_Logf(hNetlibUser, "SWMRGWaitToRead %p", this); // We can read if no threads are writing. // And there's not request to delete structure if (WAIT_OBJECT_0 == (dw = WaitForSingleObject(hFinishEV, 0))) return WAIT_FINISH; if (WAIT_FAILED == dw) return dw; dw = WaitForSingleObject(hEventNoWriter, dwTimeout); // if a request to delete became later, we should not catch it. Try once more to ask if account is not about to delete if ((dw != WAIT_FAILED) && (WAIT_OBJECT_0 == (WaitForSingleObject(hFinishEV, 0)))) { SetEvent(hEventNoWriter); return WAIT_FINISH; } if (dw == WAIT_OBJECT_0) { // This thread can read from the shared data. // Increment the number of reader threads. // But there can't be more than one thread incrementing readers, // so this is why we use semaphore. ReleaseSemaphore(hSemNumReaders, 1, &lPreviousCount); if (lPreviousCount == 0) // If this is the first reader thread, // set event to reflect this. Other reader threads can read, no writer thread can write. ResetEvent(hEventNoReaders); // Allow other writer/reader threads to use // the SWMRG synchronization object. hEventNoWrite is still non-signaled // (it looks like writer is processing thread, but it is not true) SetEvent(hEventNoWriter); } return dw; } ///////////////////////////////////////////////////////////////////////////////////////// // SWMRGDoneReading // A reader thread calls this function to let other threads // know when it no longer needs to read the shared data. void SWMRG::DoneReading() { HANDLE aHandles[2]; LONG lNumReaders; // Netlib_Logf(hNetlibUser, "SWMRGDoneReading %p", this); // We can stop reading if the events are available, // but when we stop reading we must also decrement the // number of reader threads. aHandles[0] = hEventNoWriter; aHandles[1] = hSemNumReaders; WaitForMultipleObjects(2, aHandles, TRUE, INFINITE); // Get the remaining number of readers by releasing the // semaphore and then restoring the count by immediately // performing a wait. ReleaseSemaphore(hSemNumReaders, 1, &lNumReaders); WaitForSingleObject(hSemNumReaders, INFINITE); // If there are no remaining readers, // set the event to relect this. if (lNumReaders == 0) // If there are no reader threads, // set our event to reflect this. SetEvent(hEventNoReaders); // Allow other writer/reader threads to use // the SWMRG synchronization object. // (it looks like writer is processing thread, but it is not true) SetEvent(hEventNoWriter); } ///////////////////////////////////////////////////////////////////////////////////////// // SCOUNTER SCOUNTER::SCOUNTER() { InitializeCriticalSection(&CounterCS); Event = CreateEvent(nullptr, FALSE, TRUE, nullptr); SetEvent(Event); } SCOUNTER::SCOUNTER(HANDLE InitializedEvent) { InitializeCriticalSection(&CounterCS); Event = InitializedEvent; SetEvent(Event); } SCOUNTER::~SCOUNTER() { DeleteCriticalSection(&CounterCS); CloseHandle(Event); } // Gets number value stored in SCOUNTER SO // Note you must not read the number from memory directly, because // CPU can stop reading thread when it has read HI-Word, then another thread // can change the value and then OS starts the previous thread, that reads the // LO-uint16_t of uint32_t. And the return value HI+LO-uint16_t is corrupted uint32_t SCOUNTER::GetNumber() { EnterCriticalSection(&CounterCS); uint32_t Temp = Number; LeaveCriticalSection(&CounterCS); return Temp; } // Increments SCOUNTER and unsets event // Returns Number after incrementing uint32_t SCOUNTER::Inc() { EnterCriticalSection(&CounterCS); uint32_t Temp = ++Number; ResetEvent(Event); LeaveCriticalSection(&CounterCS); return Temp; } // Decrements SCOUNTER and sets event if zero // Returns Number after decrementing uint32_t SCOUNTER::Dec() { uint32_t Temp; EnterCriticalSection(&CounterCS); if (!(Temp = --Number)) { SetEvent(Event); } LeaveCriticalSection(&CounterCS); return Temp; }