windows-nt/Source/XPSP1/NT/shell/shlwapi/tpswait.h
2020-09-26 16:20:57 +08:00

561 lines
13 KiB
C++

/*++
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);
if ((dwIndex + dwCount) < m_dwObjectCount) {
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) {
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);
}
}
};