314 lines
7.3 KiB
C++
314 lines
7.3 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
/* File: thdsync.cpp
|
|
|
|
Description: Contains classes for managing thread synchronization in
|
|
Win32 programs. Most of the work is to provide automatic unlocking
|
|
of synchronization primities on object destruction. The work on
|
|
monitors and condition variables is strongly patterned after
|
|
work in "Multithreaded Programming with Windows NT" by Pham and Garg.
|
|
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/22/97 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#include "pch.h"
|
|
#pragma hdrstop
|
|
|
|
#include "thdsync.h"
|
|
|
|
|
|
CSemaphore::CSemaphore(
|
|
DWORD dwInitialCount,
|
|
DWORD dwMaxCount
|
|
) : CWin32SyncObj(CreateSemaphore(NULL, dwInitialCount, dwMaxCount, NULL))
|
|
{
|
|
if (NULL == Handle())
|
|
throw CSyncException(CSyncException::semaphore, CSyncException::create);
|
|
}
|
|
|
|
|
|
|
|
CSemaphoreList::~CSemaphoreList(
|
|
void
|
|
)
|
|
{
|
|
for (Item *pItem = m_pFirst; NULL != pItem; pItem = m_pFirst)
|
|
{
|
|
m_pFirst = m_pFirst->m_pNext;
|
|
delete pItem->m_pSem;
|
|
delete pItem;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
CSemaphoreList::Dump(
|
|
void
|
|
)
|
|
{
|
|
DBGPRINT((TEXT("List: 0x%08X ------------------------------"), this));
|
|
DBGPRINT((TEXT("m_pFirst = 0x%08X"), m_pFirst));
|
|
DBGPRINT((TEXT("m_pLast = 0x%08X"), m_pLast));
|
|
for (Item *pItem = m_pFirst; NULL != pItem; pItem = pItem->m_pNext)
|
|
{
|
|
DBGPRINT((TEXT("\tpItem = 0x%08X"), pItem));
|
|
DBGPRINT((TEXT("\t\tm_pNext = 0x%08X"), pItem->m_pNext));
|
|
DBGPRINT((TEXT("\t\tm_pSem = 0x%08X"), pItem->m_pSem));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Insert a semaphore at the head of the list.
|
|
//
|
|
void
|
|
CSemaphoreList::Prepend(
|
|
CSemaphore *pSem
|
|
)
|
|
{
|
|
m_pFirst = new Item(pSem, m_pFirst);
|
|
if (NULL == m_pLast)
|
|
{
|
|
m_pLast = m_pFirst;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Append a semaphore to the tail of the list.
|
|
//
|
|
void
|
|
CSemaphoreList::Append(
|
|
CSemaphore *pSem
|
|
)
|
|
{
|
|
if (NULL == m_pFirst)
|
|
{
|
|
//
|
|
// Empty list. Append is same as prepend.
|
|
//
|
|
Prepend(pSem);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Create and append the new item.
|
|
//
|
|
m_pLast->m_pNext = new Item(pSem);
|
|
m_pLast = m_pLast->m_pNext;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove item from the head of the list and return the
|
|
// semaphore pointer held in the item. Delete the item.
|
|
//
|
|
CSemaphore *
|
|
CSemaphoreList::Head(
|
|
void
|
|
)
|
|
{
|
|
CSemaphore *pSem = NULL;
|
|
Item *pHead = m_pFirst; // Get first item.
|
|
if (NULL != pHead)
|
|
{
|
|
pSem = pHead->m_pSem; // Get first item's semaphore ptr.
|
|
m_pFirst = m_pFirst->m_pNext; // Unlink item from list.
|
|
|
|
delete pHead; // Delete item.
|
|
|
|
if (NULL == m_pFirst)
|
|
m_pLast = NULL; // Adjust "last" ptr if necessary.
|
|
}
|
|
return pSem; // Return semaphore ptr.
|
|
}
|
|
|
|
|
|
CMutex::CMutex(
|
|
BOOL bInitialOwner
|
|
) : CWin32SyncObj(CreateMutex(NULL, bInitialOwner, NULL))
|
|
{
|
|
if (NULL == Handle())
|
|
throw CSyncException(CSyncException::mutex, CSyncException::create);
|
|
}
|
|
|
|
CEvent::CEvent(
|
|
BOOL bManualReset,
|
|
BOOL bInitialState
|
|
) : CWin32SyncObj(CreateEvent(NULL, bManualReset, bInitialState, NULL))
|
|
{
|
|
if (NULL == Handle())
|
|
throw CSyncException(CSyncException::event, CSyncException::create);
|
|
}
|
|
|
|
|
|
//
|
|
// Release a "signal-unrgent" monitor.
|
|
//
|
|
void
|
|
CMonitorSU::Release(
|
|
void
|
|
)
|
|
{
|
|
if (0 != m_cUrgentSemCount)
|
|
{
|
|
//
|
|
// There's more than one thread waiting on the "urgent" list.
|
|
// Wake it up and let it run INSIDE of the monitor.
|
|
//
|
|
CSemaphore *pSem = m_UrgentSemList.Head();
|
|
if (NULL != pSem)
|
|
{
|
|
pSem->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Exit the monitor.
|
|
//
|
|
m_Mutex.Release();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Wait on a "signal-return" condition variable.
|
|
//
|
|
void
|
|
CConditionSR::Wait(
|
|
void
|
|
)
|
|
{
|
|
m_cSemCount++; // One more thread waiting.
|
|
m_Monitor.Release(); // Release monitor's lock to prevent deadlock.
|
|
m_Sem.Wait(); // Block until condition is signaled.
|
|
m_cSemCount--; // Have the lock, no longer waiting.
|
|
}
|
|
|
|
//
|
|
// Signal a "signal-return" condition variable.
|
|
//
|
|
void
|
|
CConditionSR::Signal(
|
|
void
|
|
)
|
|
{
|
|
//
|
|
// If any threads blocked on the condition variable, release one
|
|
// of them.
|
|
//
|
|
if (0 < m_cSemCount)
|
|
m_Sem.Release(); // Release thd's blocked on semaphore.
|
|
else
|
|
m_Monitor.Release(); // Release monitor's lock.
|
|
}
|
|
|
|
//
|
|
// Wait on a "signal-urgent" condition variable.
|
|
//
|
|
void
|
|
CConditionSU::Wait(
|
|
void
|
|
)
|
|
{
|
|
CSemaphore *pSem = new CSemaphore;
|
|
if (NULL != pSem)
|
|
{
|
|
//
|
|
// Add the semaphore to the list of waiting threads.
|
|
//
|
|
m_SemList.Append(pSem);
|
|
m_cSemCount++;
|
|
if (0 != m_Monitor.m_cUrgentSemCount)
|
|
{
|
|
//
|
|
// At least 1 thread on the "urgent" list. Get the one highest
|
|
// priority and let it run while we wait.
|
|
//
|
|
CSemaphore *pUrgentItem = m_Monitor.m_UrgentSemList.Head();
|
|
if (NULL != pUrgentItem)
|
|
{
|
|
pUrgentItem->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Exit the monitor.
|
|
//
|
|
m_Monitor.Release();
|
|
}
|
|
//
|
|
// Wait for this condition variable to be signaled.
|
|
//
|
|
pSem->Wait();
|
|
m_cSemCount--;
|
|
delete pSem;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Signal a "signal-urgent" condition variable.
|
|
//
|
|
void
|
|
CConditionSU::Signal(
|
|
void
|
|
)
|
|
{
|
|
if (0 < m_cSemCount)
|
|
{
|
|
//
|
|
// At least 1 thread waiting on this condition variable.
|
|
//
|
|
CSemaphore *pSemNew = new CSemaphore;
|
|
if (NULL != pSemNew)
|
|
{
|
|
//
|
|
// Add a new semaphore to the "urgent" list.
|
|
//
|
|
m_Monitor.m_UrgentSemList.Prepend(pSemNew);
|
|
m_Monitor.m_cUrgentSemCount++;
|
|
//
|
|
// Retrieve the next semaphore from the condition list and
|
|
// release it allowing it's thread to run.
|
|
//
|
|
CSemaphore *pSemFromList = m_SemList.Head();
|
|
if (NULL != pSemFromList)
|
|
{
|
|
pSemFromList->Release();
|
|
}
|
|
//
|
|
// Wait for this condition variable to be signaled.
|
|
//
|
|
pSemNew->Wait();
|
|
m_Monitor.m_cUrgentSemCount--;
|
|
delete pSemNew;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Wait on a Win32 mutex object.
|
|
// Throw an exception if the mutex has been abandoned or the wait has timed out.
|
|
//
|
|
void
|
|
AutoLockMutex::Wait(
|
|
DWORD dwTimeout
|
|
)
|
|
{
|
|
DWORD dwStatus = WaitForSingleObject(m_hMutex, dwTimeout);
|
|
switch(dwStatus)
|
|
{
|
|
case WAIT_ABANDONED:
|
|
throw CSyncException(CSyncException::mutex, CSyncException::abandoned);
|
|
break;
|
|
case WAIT_TIMEOUT:
|
|
throw CSyncException(CSyncException::mutex, CSyncException::timeout);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} |