522 lines
10 KiB
C++
522 lines
10 KiB
C++
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
tpstimer.h
|
|
|
|
Abstract:
|
|
|
|
Timer classes. Moved out of tpsclass.h
|
|
|
|
Contents:
|
|
CTimer
|
|
CTimerQueueEntry
|
|
CTimerQueueList
|
|
CTimerQueue
|
|
CTimerRequest
|
|
CTimerQueueDeleteRequest
|
|
CTimerAddRequest
|
|
CTimerChangeRequest
|
|
CTimerCancelRequest
|
|
|
|
Author:
|
|
|
|
Richard L Firth (rfirth) 08-Aug-1998
|
|
|
|
Revision History:
|
|
|
|
08-Aug-1998 rfirth
|
|
Created
|
|
|
|
--*/
|
|
|
|
//
|
|
// manifests
|
|
//
|
|
|
|
#define TPS_TIMER_IN_USE 0x80000000
|
|
#define TPS_TIMER_CANCELLED 0x40000000
|
|
|
|
//
|
|
// external data
|
|
//
|
|
|
|
extern LONG g_UID;
|
|
|
|
//
|
|
// classes
|
|
//
|
|
|
|
//
|
|
// CTimer
|
|
//
|
|
|
|
class CTimer {
|
|
|
|
private:
|
|
|
|
HANDLE m_hQueue; // address of owning queue
|
|
HANDLE m_hTimer; // timer ordinal
|
|
WAITORTIMERCALLBACKFUNC m_pfnCallback;
|
|
LPVOID m_pContext;
|
|
DWORD m_dwPeriod;
|
|
DWORD m_dwFlags;
|
|
|
|
public:
|
|
|
|
CTimer(HANDLE hQueue,
|
|
WAITORTIMERCALLBACKFUNC pfnCallback,
|
|
LPVOID pContext,
|
|
DWORD dwPeriod,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
m_hQueue = hQueue;
|
|
|
|
//
|
|
// FEATURE - (prevent this scenario) not industrial-strength: can have 2 timers with same ID
|
|
//
|
|
|
|
m_hTimer = IntToPtr(InterlockedIncrement(&g_UID));
|
|
m_pfnCallback = pfnCallback;
|
|
m_pContext = pContext;
|
|
m_dwPeriod = dwPeriod;
|
|
m_dwFlags = dwFlags;
|
|
}
|
|
|
|
HANDLE GetHandle(VOID) const {
|
|
return m_hTimer;
|
|
}
|
|
|
|
HANDLE GetQueue(VOID) const {
|
|
return m_hQueue;
|
|
}
|
|
|
|
VOID Execute(VOID) {
|
|
if (m_dwFlags & TPS_EXECUTEIO) {
|
|
|
|
//
|
|
// NT code does nothing with this flag. We should queue
|
|
// request to I/O worker thread
|
|
//
|
|
|
|
ASSERT(!(m_dwFlags & TPS_EXECUTEIO));
|
|
|
|
}
|
|
m_pfnCallback(m_pContext, TRUE);
|
|
}
|
|
|
|
VOID SetPeriod(DWORD dwPeriod) {
|
|
m_dwPeriod = dwPeriod;
|
|
}
|
|
|
|
DWORD GetPeriod(VOID) const {
|
|
return m_dwPeriod;
|
|
}
|
|
|
|
BOOL IsOneShot(VOID) {
|
|
return GetPeriod() == 0;
|
|
}
|
|
|
|
VOID SetInUse(VOID) {
|
|
m_dwFlags |= TPS_TIMER_IN_USE;
|
|
}
|
|
|
|
VOID ResetInUse(VOID) {
|
|
m_dwFlags &= ~TPS_TIMER_IN_USE;
|
|
}
|
|
|
|
BOOL IsInUse(VOID) {
|
|
return (m_dwFlags & TPS_TIMER_IN_USE) ? TRUE : FALSE;
|
|
}
|
|
|
|
VOID SetCancelled(VOID) {
|
|
m_dwFlags |= TPS_TIMER_CANCELLED;
|
|
}
|
|
|
|
BOOL IsCancelled(VOID) {
|
|
return (m_dwFlags & TPS_TIMER_CANCELLED) ? TRUE : FALSE;
|
|
}
|
|
};
|
|
|
|
//
|
|
// CTimerQueueEntry
|
|
//
|
|
|
|
class CTimerQueueEntry : public CTimedListEntry, public CTimer {
|
|
|
|
private:
|
|
|
|
public:
|
|
|
|
CDoubleLinkedList m_TimerList;
|
|
|
|
CTimerQueueEntry(HANDLE hQueue,
|
|
WAITORTIMERCALLBACKFUNC pfnCallback,
|
|
LPVOID pContext,
|
|
DWORD dwDueTime,
|
|
DWORD dwPeriod,
|
|
DWORD dwFlags
|
|
) :
|
|
CTimedListEntry(dwDueTime),
|
|
CTimer(hQueue,
|
|
pfnCallback,
|
|
pContext,
|
|
dwPeriod,
|
|
dwFlags
|
|
)
|
|
{
|
|
CDoubleLinkedListEntry::Init();
|
|
m_TimerList.Init();
|
|
}
|
|
|
|
~CTimerQueueEntry() {
|
|
m_TimerList.Remove();
|
|
}
|
|
|
|
VOID SetPeriodicTime(VOID) {
|
|
SetTimeStamp(ExpiryTime());
|
|
SetWaitTime(GetPeriod());
|
|
}
|
|
|
|
CDoubleLinkedList * TimerListHead(VOID) {
|
|
return m_TimerList.Head();
|
|
}
|
|
};
|
|
|
|
//
|
|
// CTimerQueueList
|
|
//
|
|
|
|
class CTimerQueueList {
|
|
|
|
private:
|
|
|
|
CDoubleLinkedList m_QueueList;
|
|
CDoubleLinkedList m_TimerList;
|
|
|
|
public:
|
|
|
|
VOID Init(VOID) {
|
|
m_QueueList.Init();
|
|
m_TimerList.Init();
|
|
}
|
|
|
|
CDoubleLinkedList * QueueListHead(VOID) {
|
|
return m_QueueList.Head();
|
|
}
|
|
|
|
CDoubleLinkedList * TimerListHead(VOID) {
|
|
return m_TimerList.Head();
|
|
}
|
|
|
|
CDoubleLinkedListEntry * FindQueue(CDoubleLinkedListEntry * pEntry) {
|
|
return m_QueueList.FindEntry(pEntry);
|
|
}
|
|
|
|
BOOL Wait(VOID) {
|
|
|
|
DWORD dwWaitTime = INFINITE;
|
|
CTimedListEntry * pTimer = (CTimedListEntry * )m_TimerList.Next();
|
|
|
|
ASSERT(pTimer != NULL);
|
|
|
|
if (pTimer != (CTimedListEntry * )m_TimerList.Head()) {
|
|
dwWaitTime = pTimer->TimeToWait();
|
|
}
|
|
|
|
//
|
|
// HACKHACK (tnoonan): Can't just check for 0 since
|
|
// Win95 will always return WAIT_TIMEOUT (despite what
|
|
// the docs say).
|
|
//
|
|
|
|
DWORD dwResult = SleepEx(dwWaitTime, TRUE);
|
|
|
|
return (dwResult == 0) || (dwResult == WAIT_TIMEOUT);
|
|
}
|
|
|
|
VOID ProcessCompletions(VOID) {
|
|
|
|
//
|
|
// run down list of all timers; for each expired timer, execute its
|
|
// completion handler. If one-shot timer, delete it, else reset the
|
|
// timer and re-insert it in the list
|
|
//
|
|
// If a timer is re-inserted further down the list, we may visit it
|
|
// again before we have completed the traversal. This is OK: either it
|
|
// has already expired, in which case we execute again, or it hasn't
|
|
// expired, in which case we terminate the traversal
|
|
//
|
|
|
|
CTimerQueueEntry * pTimer;
|
|
CTimerQueueEntry * pNext = (CTimerQueueEntry *)m_TimerList.Next();
|
|
|
|
do {
|
|
pTimer = pNext;
|
|
if ((pTimer == (CTimerQueueEntry *)m_TimerList.Head())
|
|
|| !pTimer->IsTimedOut()) {
|
|
break;
|
|
}
|
|
pNext = (CTimerQueueEntry * )pTimer->Next();
|
|
pTimer->Remove();
|
|
pTimer->SetInUse();
|
|
pTimer->Execute();
|
|
if (pTimer->IsOneShot() || pTimer->IsCancelled()) {
|
|
delete pTimer;
|
|
} else {
|
|
pTimer->SetPeriodicTime();
|
|
pTimer->ResetInUse();
|
|
pTimer->InsertBack(m_TimerList.Head());
|
|
}
|
|
} while (TRUE);
|
|
}
|
|
};
|
|
|
|
//
|
|
// CTimerQueue
|
|
//
|
|
|
|
class CTimerQueue : public CDoubleLinkedList {
|
|
|
|
private:
|
|
|
|
CDoubleLinkedList m_TimerList;
|
|
|
|
public:
|
|
|
|
CTimerQueue(CTimerQueueList * pList) {
|
|
CDoubleLinkedList::Init();
|
|
m_TimerList.Init();
|
|
InsertTail(pList->QueueListHead());
|
|
}
|
|
|
|
~CTimerQueue() {
|
|
Remove();
|
|
}
|
|
|
|
CDoubleLinkedList * TimerListHead(VOID) {
|
|
return m_TimerList.Head();
|
|
}
|
|
|
|
CTimerQueueEntry * FindTimer(HANDLE hTimer) {
|
|
|
|
CDoubleLinkedListEntry * pEntry;
|
|
|
|
for (pEntry = m_TimerList.Next();
|
|
pEntry != m_TimerList.Head();
|
|
pEntry = pEntry->Next()) {
|
|
|
|
CTimerQueueEntry * pTimer;
|
|
|
|
pTimer = CONTAINING_RECORD(pEntry, CTimerQueueEntry, m_TimerList);
|
|
if (pTimer->GetHandle() == hTimer) {
|
|
return pTimer;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
VOID DeleteTimers(VOID) {
|
|
|
|
CDoubleLinkedListEntry * pEntry;
|
|
|
|
for (pEntry = m_TimerList.Next();
|
|
pEntry != m_TimerList.Head();
|
|
pEntry = m_TimerList.Next()) {
|
|
|
|
CTimerQueueEntry * pTimer;
|
|
|
|
pTimer = CONTAINING_RECORD(pEntry, CTimerQueueEntry, m_TimerList);
|
|
|
|
//
|
|
// remove timer from global timer list (linked on CDoubleLinkedList)
|
|
//
|
|
|
|
pTimer->Remove();
|
|
|
|
//
|
|
// timer will be removed from m_TimerList by its destructor
|
|
//
|
|
|
|
delete pTimer;
|
|
}
|
|
}
|
|
};
|
|
|
|
//
|
|
// CTimerRequest
|
|
//
|
|
|
|
class CTimerRequest {
|
|
|
|
private:
|
|
|
|
BOOL m_bCompleted;
|
|
DWORD m_dwStatus;
|
|
|
|
public:
|
|
|
|
CTimerRequest() {
|
|
m_bCompleted = FALSE;
|
|
m_dwStatus = ERROR_SUCCESS;
|
|
}
|
|
|
|
VOID SetComplete(VOID) {
|
|
m_bCompleted = TRUE;
|
|
}
|
|
|
|
VOID WaitForCompletion(VOID) {
|
|
while (!m_bCompleted) {
|
|
SleepEx(0, TRUE);
|
|
}
|
|
}
|
|
|
|
VOID SetStatus(DWORD dwStatus) {
|
|
m_dwStatus = dwStatus;
|
|
}
|
|
|
|
VOID SetCompletionStatus(DWORD dwStatus) {
|
|
SetStatus(dwStatus);
|
|
SetComplete();
|
|
}
|
|
|
|
BOOL SetThreadStatus(VOID) {
|
|
if (m_dwStatus == ERROR_SUCCESS) {
|
|
return TRUE;
|
|
}
|
|
SetLastError(m_dwStatus);
|
|
return FALSE;
|
|
}
|
|
};
|
|
|
|
//
|
|
// CTimerQueueDeleteRequest
|
|
//
|
|
|
|
class CTimerQueueDeleteRequest : public CTimerRequest {
|
|
|
|
private:
|
|
|
|
HANDLE m_hQueue;
|
|
|
|
public:
|
|
|
|
CTimerQueueDeleteRequest(HANDLE hQueue) : CTimerRequest() {
|
|
m_hQueue = hQueue;
|
|
}
|
|
|
|
HANDLE GetQueue(VOID) const {
|
|
return m_hQueue;
|
|
}
|
|
};
|
|
|
|
//
|
|
// CTimerAddRequest
|
|
//
|
|
|
|
class CTimerAddRequest : public CTimerQueueEntry, public CTimerRequest {
|
|
|
|
public:
|
|
|
|
CTimerAddRequest(HANDLE hQueue,
|
|
WAITORTIMERCALLBACKFUNC pfnCallback,
|
|
LPVOID pContext,
|
|
DWORD dwDueTime,
|
|
DWORD dwPeriod,
|
|
DWORD dwFlags
|
|
) :
|
|
CTimerQueueEntry(hQueue,
|
|
pfnCallback,
|
|
pContext,
|
|
dwDueTime,
|
|
dwPeriod,
|
|
dwFlags
|
|
),
|
|
CTimerRequest()
|
|
{
|
|
}
|
|
|
|
HANDLE GetHandle(VOID) const {
|
|
return CTimer::GetHandle();
|
|
}
|
|
|
|
CTimerQueue * GetQueue(VOID) const {
|
|
return (CTimerQueue *)CTimer::GetQueue();
|
|
}
|
|
};
|
|
|
|
//
|
|
// CTimerChangeRequest
|
|
//
|
|
|
|
class CTimerChangeRequest : public CTimerRequest {
|
|
|
|
private:
|
|
|
|
HANDLE m_hQueue;
|
|
HANDLE m_hTimer;
|
|
DWORD m_dwDueTime;
|
|
DWORD m_dwPeriod;
|
|
|
|
public:
|
|
|
|
CTimerChangeRequest(HANDLE hQueue,
|
|
HANDLE hTimer,
|
|
DWORD dwDueTime,
|
|
DWORD dwPeriod
|
|
) :
|
|
CTimerRequest()
|
|
{
|
|
m_hQueue = hQueue;
|
|
m_hTimer = hTimer;
|
|
m_dwDueTime = dwDueTime;
|
|
m_dwPeriod = dwPeriod;
|
|
}
|
|
|
|
HANDLE GetQueue(VOID) const {
|
|
return m_hQueue;
|
|
}
|
|
|
|
HANDLE GetTimer(VOID) const {
|
|
return m_hTimer;
|
|
}
|
|
|
|
DWORD GetDueTime(VOID) const {
|
|
return m_dwDueTime;
|
|
}
|
|
|
|
DWORD GetPeriod(VOID) const {
|
|
return m_dwPeriod;
|
|
}
|
|
};
|
|
|
|
//
|
|
// CTimerCancelRequest
|
|
//
|
|
|
|
class CTimerCancelRequest : public CTimerRequest {
|
|
|
|
private:
|
|
|
|
HANDLE m_hQueue;
|
|
HANDLE m_hTimer;
|
|
|
|
public:
|
|
|
|
CTimerCancelRequest(HANDLE hQueue, HANDLE hTimer) : CTimerRequest() {
|
|
m_hQueue = hQueue;
|
|
m_hTimer = hTimer;
|
|
}
|
|
|
|
HANDLE GetQueue(VOID) const {
|
|
return m_hQueue;
|
|
}
|
|
|
|
HANDLE GetTimer(VOID) const {
|
|
return m_hTimer;
|
|
}
|
|
};
|