/*++ Copyright (c) 1995 Microsoft Corporation Module Name: net\routing\ipx\sap\timermgr.c Abstract: Timer queue manager for SAP agent. Author: Vadim Eydelman 05-15-1995 Revision History: --*/ #include "sapp.h" // Timer queues and associated synchronization typedef struct _TIMER_QUEUES { LIST_ENTRY hrQueue; // Hi-res quueue (msec) LIST_ENTRY lrQueue; // Lo-res queue (sec) HANDLE timer; // NT timer signalled when // one or more items in the // timer queue have expired CRITICAL_SECTION lock; // Protection } TIMER_QUEUES, *PTIMER_QUEUES; TIMER_QUEUES TimerQueues; /*++ ******************************************************************* C r e a t e T i m e r Q u e u e Routine Description: Allocates resources for timer queue Arguments: wakeObject - sync object, to be signalled when timer manager needs a shot process its timer queue Return Value: NO_ERROR - resources were allocated successfully other - reason of failure (windows error code) ******************************************************************* --*/ DWORD IpxSapCreateTimerQueue ( HANDLE *wakeObject ) { DWORD status; status = NtCreateTimer (&TimerQueues.timer, TIMER_ALL_ACCESS, NULL, NotificationTimer); if (NT_SUCCESS (status)) { *wakeObject = TimerQueues.timer; InitializeCriticalSection (&TimerQueues.lock); InitializeListHead (&TimerQueues.hrQueue); InitializeListHead (&TimerQueues.lrQueue); return NO_ERROR; } else Trace (DEBUG_FAILURES, "File: %s, line %ld." " Failed to create timer (nts:%lx).", __FILE__, __LINE__, status); return status; } /*++ ******************************************************************* E x p i r e T i m e r Q u e u e Routine Description: Expires (completes) all requests in timer queue Arguments: None Return Value: None ******************************************************************* --*/ VOID ExpireTimerQueue ( void ) { BOOLEAN res; Trace (DEBUG_TIMER, "Expiring timer queue."); EnterCriticalSection (&TimerQueues.lock); while (!IsListEmpty (&TimerQueues.hrQueue)) { PTM_PARAM_BLOCK treq = CONTAINING_RECORD (TimerQueues.hrQueue.Flink, TM_PARAM_BLOCK, link); RemoveEntryList (&treq->link); ProcessCompletedTimerRequest (treq); } while (!IsListEmpty (&TimerQueues.lrQueue)) { PTM_PARAM_BLOCK treq = CONTAINING_RECORD (TimerQueues.lrQueue.Flink, TM_PARAM_BLOCK, link); RemoveEntryList (&treq->link); ProcessCompletedTimerRequest (treq); } LeaveCriticalSection (&TimerQueues.lock); } /*++ ******************************************************************* E x p i r e L R R e q u s t s Routine Description: Expires (completes) Low Resolution timer requests that return true from expiration check routine Arguments: context - context to pass to expiration check routine Return Value: None ******************************************************************* --*/ VOID ExpireLRRequests ( PVOID context ) { BOOLEAN res; PLIST_ENTRY cur; Trace (DEBUG_TIMER, "Expire LR timer request call with context %08lx.", context); EnterCriticalSection (&TimerQueues.lock); cur = TimerQueues.lrQueue.Flink; while (cur!=&TimerQueues.lrQueue) { PTM_PARAM_BLOCK treq = CONTAINING_RECORD (cur, TM_PARAM_BLOCK, link); cur = cur->Flink; if ((treq->ExpirationCheckProc!=NULL) && (*treq->ExpirationCheckProc)(treq, context)) { RemoveEntryList (&treq->link); ProcessCompletedTimerRequest (treq); } } LeaveCriticalSection (&TimerQueues.lock); } /*++ ******************************************************************* D e l e t e T i m e r Q u e u e Routine Description: Release all resources associated with timer queue Arguments: None Return Value: NO_ERROR - operation completed OK ******************************************************************* --*/ VOID IpxSapDeleteTimerQueue ( void ) { NtClose (TimerQueues.timer); DeleteCriticalSection (&TimerQueues.lock); } /*++ ******************************************************************* P r o c e s s T i m e r Q u e u e Routine Description: Process timer queues and moves expired requests to completion queue This routine should be called when wake object is signalled Arguments: None Return Value: None ******************************************************************* --*/ VOID ProcessTimerQueue ( void ) { ULONG curTime = GetTickCount (); ULONG dueTime = curTime+MAXULONG/2; LONGLONG timeout; DWORD status; EnterCriticalSection (&TimerQueues.lock); while (!IsListEmpty (&TimerQueues.hrQueue)) { PTM_PARAM_BLOCK treq = CONTAINING_RECORD (TimerQueues.hrQueue.Flink, TM_PARAM_BLOCK, link); if (IsLater(curTime,treq->dueTime)) { RemoveEntryList (&treq->link); ProcessCompletedTimerRequest (treq); } else { dueTime = treq->dueTime; break; } } while (!IsListEmpty (&TimerQueues.lrQueue)) { PTM_PARAM_BLOCK treq = CONTAINING_RECORD (TimerQueues.lrQueue.Flink, TM_PARAM_BLOCK, link); if (IsLater(curTime,treq->dueTime)) { RemoveEntryList (&treq->link); ProcessCompletedTimerRequest (treq); } else { if (IsLater(dueTime,treq->dueTime)) dueTime = treq->dueTime; break; } } timeout = ((LONGLONG)(dueTime-curTime))*(-10000); status = NtSetTimer (TimerQueues.timer, (PLARGE_INTEGER)&timeout, NULL, NULL, FALSE, 0, NULL); ASSERTMSG ("Could not set timer ", NT_SUCCESS (status)); LeaveCriticalSection (&TimerQueues.lock); } /*++ ******************************************************************* A d d H R T i m e r R e q u e s t Routine Description: Enqueue request for hi-res timer (delay in order of msec) Arguments: treq - timer parameter block: dueTime field must be set Return Value: None ******************************************************************* --*/ VOID AddHRTimerRequest ( PTM_PARAM_BLOCK treq ) { PLIST_ENTRY cur; EnterCriticalSection (&TimerQueues.lock); cur = TimerQueues.hrQueue.Blink; while (cur!=&TimerQueues.hrQueue) { PTM_PARAM_BLOCK node = CONTAINING_RECORD (cur, TM_PARAM_BLOCK, link); if (IsLater(treq->dueTime,node->dueTime)) break; cur = cur->Blink; } InsertHeadList (cur, &treq->link); if (cur==&TimerQueues.hrQueue) { ULONG delay = treq->dueTime-GetTickCount (); LONGLONG timeout = (delay>MAXULONG/2) ? 0 : ((LONGLONG)delay*(-10000)); DWORD status = NtSetTimer (TimerQueues.timer, (PLARGE_INTEGER)&timeout, NULL, NULL, FALSE, 0, NULL); ASSERTMSG ("Could not set timer ", NT_SUCCESS (status)); } LeaveCriticalSection (&TimerQueues.lock); } /*++ ******************************************************************* A d d L R T i m e r R e q u e s t Routine Description: Enqueue request for lo-res timer (delay in order of sec) Arguments: treq - timer parameter block: dueTime field must be set Return Value: None ******************************************************************* --*/ VOID AddLRTimerRequest ( PTM_PARAM_BLOCK treq ) { PLIST_ENTRY cur; RoundUpToSec (treq->dueTime); EnterCriticalSection (&TimerQueues.lock); cur = TimerQueues.lrQueue.Blink; while (cur!=&TimerQueues.lrQueue) { PTM_PARAM_BLOCK node = CONTAINING_RECORD (cur, TM_PARAM_BLOCK, link); if (IsLater(treq->dueTime,node->dueTime)) break; cur = cur->Blink; } InsertHeadList (cur, &treq->link); if ((cur==&TimerQueues.lrQueue) && IsListEmpty (&TimerQueues.hrQueue)) { ULONG delay = treq->dueTime-GetTickCount (); LONGLONG timeout = (delay>MAXULONG/2) ? 0 : ((LONGLONG)delay*(-10000)); DWORD status = NtSetTimer (TimerQueues.timer, (PLARGE_INTEGER)&timeout, NULL, NULL, FALSE, 0, NULL); ASSERTMSG ("Could not set timer ", NT_SUCCESS (status)); } LeaveCriticalSection (&TimerQueues.lock); }