windows-nt/Source/XPSP1/NT/shell/osshell/dskquota/common/thdsync.cpp
2020-09-26 16:20:57 +08:00

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