|
|
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
tpswait.h
Abstract:
Wait classes. Moved out of tpsclass.h
Contents: CWait CWaitRequest CWaitAddRequest CWaitRemoveRequest CWaitThreadInfo
Author:
Richard L Firth (rfirth) 08-Aug-1998
Revision History:
08-Aug-1998 rfirth Created
--*/
//
// forward declarations
//
class CWaitThreadInfo;
//
// classes
//
//
// CWait
//
class CWait : public CTimedListEntry {
private:
HANDLE m_hObject; WAITORTIMERCALLBACKFUNC m_pCallback; LPVOID m_pContext; CWaitThreadInfo * m_pThreadInfo; DWORD m_dwFlags;
public:
CWait(HANDLE hObject, WAITORTIMERCALLBACKFUNC pCallback, LPVOID pContext, DWORD dwWaitTime, DWORD dwFlags, CWaitThreadInfo * pInfo ) : CTimedListEntry(dwWaitTime) { m_hObject = hObject; m_pCallback = pCallback; m_pContext = pContext; m_pThreadInfo = pInfo; m_dwFlags = dwFlags; }
CWait() { }
CWait * Next(VOID) { return (CWait *)CTimedListEntry::Next(); }
CWaitThreadInfo * GetThreadInfo(VOID) const { return m_pThreadInfo; }
VOID Execute(BOOL bTimeout) {
//
// execute function in this thread if required to do so, else we run
// the callback in a non-I/O worker thread
//
//
// APPCOMPAT - can't do this: the callback types for Wait & Work requests
// are different: one takes 2 parameters, the other one. We
// can't make this change until this issue is resolved with
// NT guys
//
//if (m_dwFlags & WT_EXECUTEINWAITTHREAD) {
m_pCallback(m_pContext, bTimeout != 0); //} else {
//
// //
// // would have to allocate object from heap to hold callback
// // function, context & bTimeout parameters in order to pass
// // them to worker thread (we only have access to one APC
// // parameter and we'd have to nominate different APC)
// //
//
// Ie_QueueUserWorkItem((LPTHREAD_START_ROUTINE)m_pCallback,
// m_pContext,
// FALSE
// );
//}
}
HANDLE GetHandle(VOID) const { return m_hObject; }
BOOL IsNoRemoveItem(VOID) { return (m_dwFlags & SRWSO_NOREMOVE) ? TRUE : FALSE; } };
//
// CWaitRequest
//
class CWaitRequest {
private:
BOOL m_bCompleted; CWait * m_pWait;
public:
CWaitRequest() { m_bCompleted = FALSE; }
CWaitRequest(CWait * pWait) { m_bCompleted = FALSE; m_pWait = pWait; }
VOID SetComplete(VOID) { m_bCompleted = TRUE; }
VOID WaitForCompletion(VOID) { while (!m_bCompleted) { SleepEx(0, TRUE); } }
VOID SetWaitPointer(CWait * pWait) { m_pWait = pWait; }
CWait * GetWaitPointer(VOID) const { return m_pWait; } };
//
// CWaitAddRequest
//
class CWaitAddRequest : public CWait, public CWaitRequest {
public:
CWaitAddRequest(HANDLE hObject, WAITORTIMERCALLBACKFUNC pCallback, LPVOID pContext, DWORD dwWaitTime, DWORD dwFlags, CWaitThreadInfo * pInfo ) : CWait(hObject, pCallback, pContext, dwWaitTime, dwFlags, pInfo), CWaitRequest() { } };
//
// CWaitRemoveRequest
//
class CWaitRemoveRequest : public CWaitRequest {
public:
CWaitRemoveRequest(HANDLE hWait) : CWaitRequest((CWait *)hWait) { } };
//
// CWaitThreadInfo
//
class CWaitThreadInfo : public CDoubleLinkedList, public CCriticalSection {
private:
HANDLE m_hThread; DWORD m_dwObjectCount; HANDLE m_Objects[MAXIMUM_WAIT_OBJECTS]; CWait * m_pWaiters[MAXIMUM_WAIT_OBJECTS]; CWait m_Waiters[MAXIMUM_WAIT_OBJECTS]; CDoubleLinkedList m_FreeList; CDoubleLinkedList m_WaitList;
public:
CWaitThreadInfo(CDoubleLinkedList * pList) { CDoubleLinkedList::Init(); m_hThread = NULL; m_dwObjectCount = 0; m_FreeList.Init(); m_WaitList.Init(); for (int i = 0; i < ARRAY_ELEMENTS(m_Waiters); ++i) { m_Waiters[i].InsertTail(&m_FreeList); } InsertHead(pList); }
VOID SetHandle(HANDLE hThread) { m_hThread = hThread; }
HANDLE GetHandle(VOID) const { return m_hThread; }
DWORD GetObjectCount(VOID) const { return m_dwObjectCount; }
BOOL IsAvailableEntry(VOID) const { return m_dwObjectCount < ARRAY_ELEMENTS(m_Objects); }
BOOL IsInvalidHandle(DWORD dwIndex) {
ASSERT(dwIndex < m_dwObjectCount);
//
// GetHandleInformation() doesn't exist on Win95
//
//
//DWORD dwHandleFlags;
//
//return !GetHandleInformation(m_Objects[dwIndex], &dwHandleFlags);
DWORD status = WaitForSingleObject(m_Objects[dwIndex], 0);
if ((status == WAIT_FAILED) && (GetLastError() == ERROR_INVALID_HANDLE)) { //#if DBG
//char buf[128];
//wsprintf(buf, "IsInvalidHandle(%d): handle %#x is invalid\n", dwIndex, m_Objects[dwIndex]);
//OutputDebugString(buf);
//#endif
return TRUE; } return FALSE; }
VOID Compress(DWORD dwIndex, DWORD dwCount = 1) { ASSERT(dwCount != 0); ASSERT((int)m_dwObjectCount > 0); ASSERT(m_dwObjectCount < MAXIMUM_WAIT_OBJECTS);
if (((dwIndex + dwCount) < m_dwObjectCount) && (m_dwObjectCount < MAXIMUM_WAIT_OBJECTS)) { RtlMoveMemory(&m_Objects[dwIndex], &m_Objects[dwIndex + dwCount], sizeof(m_Objects[0]) * (m_dwObjectCount - (dwIndex + dwCount)) ); RtlMoveMemory(&m_pWaiters[dwIndex], &m_pWaiters[dwIndex + dwCount], sizeof(m_pWaiters[0]) * (m_dwObjectCount - (dwIndex + dwCount)) ); } m_dwObjectCount -= dwCount; }
VOID Expand(DWORD dwIndex) { ASSERT((int)m_dwObjectCount > 0); // The off by one is because we copying from dwIndex + 1 and our size to copy is
// m_dwObjectCount - dwIndex
if (m_dwObjectCount < MAXIMUM_WAIT_OBJECTS - 1) { RtlMoveMemory(&m_Objects[dwIndex], &m_Objects[dwIndex + 1], sizeof(m_Objects[0]) * (m_dwObjectCount - dwIndex) ); RtlMoveMemory(&m_pWaiters[dwIndex], &m_pWaiters[dwIndex + 1], sizeof(m_pWaiters[0]) * (m_dwObjectCount - dwIndex) ); ++m_dwObjectCount; }
ASSERT(m_dwObjectCount <= ARRAY_ELEMENTS(m_Objects)); }
//DWORD BuildList(VOID) {
//
// //
// // PERF: only rebuild from changed index
// //
//
// m_dwObjectCount = 0;
// for (CWait * pWait = (CWait *)m_WaitList.Next();
// pWait = pWait->Next();
// !m_WaitList.IsHead(pWait)) {
// m_pWaiters[m_dwObjectCount] = pWait;
// m_Objects[m_dwObjectCount] = pWait->GetHandle();
// ++m_dwObjectCount;
// }
// return GetWaitTime();
//}
DWORD Wait(DWORD dwTimeout = INFINITE) {
//
// if no objects in list, sleep alertably for the timeout period
//
if (m_dwObjectCount == 0) { SleepEx(dwTimeout, TRUE); return WAIT_IO_COMPLETION; }
//
// else wait alertably for the timeout period
//
ASSERT(m_dwObjectCount <= ARRAY_ELEMENTS(m_Objects));
return WaitForMultipleObjectsEx(m_dwObjectCount, m_Objects, FALSE, // fWaitAll
dwTimeout, TRUE // fAlertable
); }
DWORD GetWaitTime(VOID) {
ASSERT(m_dwObjectCount <= ARRAY_ELEMENTS(m_Objects));
if (m_dwObjectCount != 0) {
CWait * pWaiter = m_pWaiters[0]; DWORD dwWaitTime = pWaiter->GetWaitTime();
if (dwWaitTime != INFINITE) {
DWORD dwTimeNow = GetTickCount(); DWORD dwTimeStamp = pWaiter->GetTimeStamp();
if (dwTimeNow > dwTimeStamp + dwWaitTime) {
//
// first object expired already
//
return 0; }
//
// number of milliseconds until next waiter expires
//
return (dwTimeStamp + dwWaitTime) - dwTimeNow; } }
//
// nothing in list
//
return INFINITE; }
CWait * GetFreeWaiter(VOID) { return (CWait *)m_FreeList.RemoveHead(); }
VOID InsertWaiter(CWait * pWait) {
DWORD dwIndex = 0; BOOL bAtEnd = TRUE; CDoubleLinkedListEntry * pHead = m_WaitList.Head();
ASSERT(m_dwObjectCount <= ARRAY_ELEMENTS(m_Objects));
if ((m_dwObjectCount != 0) && !pWait->IsInfiniteTimeout()) {
//
// not infinite timeout. Find place in list to insert this object
//
//
// PERF: typically, new wait will be longer than most currently in
// list, so should start from end of non-infinite timeouts
// and work backwards
//
for (; dwIndex < m_dwObjectCount; ++dwIndex) { if (pWait->ExpiryTime() < m_pWaiters[dwIndex]->ExpiryTime()) { pHead = m_pWaiters[dwIndex]->Head(); bAtEnd = (dwIndex == (m_dwObjectCount - 1)); break; } } }
//
// insert the new wait object at the correct location
//
pWait->InsertTail(pHead); if (!bAtEnd && (m_dwObjectCount != 0)) { Expand(dwIndex); } else { dwIndex = m_dwObjectCount; ++m_dwObjectCount; }
//
// update object list and pointer list
//
m_Objects[dwIndex] = pWait->GetHandle(); m_pWaiters[dwIndex] = pWait; }
VOID RemoveWaiter(CWait * pWait, DWORD dwIndex) {
//
// remove the waiter from the wait list and add it back to the
// free list
//
pWait->Remove(); pWait->InsertTail(&m_FreeList);
//
// if the object was not at the end of the list then compress
// the list
//
if (dwIndex != (m_dwObjectCount - 1)) { Compress(dwIndex, 1); } else { --m_dwObjectCount; } }
VOID RemoveWaiter(DWORD dwIndex) {
ASSERT(m_dwObjectCount <= ARRAY_ELEMENTS(m_Objects));
RemoveWaiter(m_pWaiters[dwIndex], dwIndex); }
BOOL RemoveWaiter(CWait * pWait) {
ASSERT(m_dwObjectCount <= ARRAY_ELEMENTS(m_Objects));
for (DWORD dwIndex = 0; dwIndex < m_dwObjectCount; ++dwIndex) { if (m_pWaiters[dwIndex] == pWait) { RemoveWaiter(pWait, dwIndex); return TRUE; } } return FALSE; }
VOID ProcessTimeouts(VOID) {
DWORD dwTimeNow = GetTickCount(); DWORD dwCount = 0;
ASSERT(m_dwObjectCount <= ARRAY_ELEMENTS(m_Objects));
while (dwCount < m_dwObjectCount) {
CWait * pWait = m_pWaiters[dwCount];
//
// if waiter has expired, invoke its callback then remove it from
// the wait list and add back to the free list
//
if (pWait->IsTimedOut(dwTimeNow)) { pWait->Execute(TRUE); pWait->Remove(); pWait->InsertTail(&m_FreeList); ++dwCount; } else {
//
// quit loop at first non-timed-out entry
//
break; } }
ASSERT(dwCount != 0);
if (dwCount != 0) { Compress(0, dwCount); } }
VOID PurgeInvalidHandles(VOID) {
DWORD dwCount = 0; DWORD dwIndex = 0; DWORD dwIndexStart = 0;
ASSERT(m_dwObjectCount <= ARRAY_ELEMENTS(m_Objects));
while (dwIndex < m_dwObjectCount) {
CWait * pWait = m_pWaiters[dwIndex];
//
// if handle has become invalid, invoke the callback then remove it
// from the wait list and add back to the free list
//
if (IsInvalidHandle(dwIndex)) { pWait->Execute(FALSE); pWait->Remove(); pWait->InsertTail(&m_FreeList); if (dwIndexStart == 0) { dwIndexStart = dwIndex; } ++dwCount; } else if (dwCount != 0) { Compress(dwIndexStart, dwCount); dwIndex = dwIndexStart - 1; dwIndexStart = 0; dwCount = 0; } ++dwIndex; } if (dwCount != 0) { Compress(dwIndexStart, dwCount); } }
VOID ProcessCompletion(DWORD dwIndex) {
CWait * pWait = m_pWaiters[dwIndex];
pWait->Execute(FALSE); if (!pWait->IsNoRemoveItem()) { RemoveWaiter(dwIndex); } } };
|