/*++ 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; } };