|
|
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
NtTimer.c
Abstract:
This module implements the nt version of the timer and worker thread management routines. These services are provided to all mini redirector writers. The timer service comes in two flavours - a periodic trigger and a one shot notification.
Author:
Joe Linn [JoeLinn] 2-mar-95
Revision History:
Balan Sethu Raman [SethuR] 7-Mar-95 Included one shot, periodic notification for work queue items.
--*/
#include "precomp.h"
#pragma hdrstop
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, RxInitializeRxTimer)
#pragma alloc_text(PAGE, RxTearDownRxTimer)
#pragma alloc_text(PAGE, RxPostRecurrentTimerRequest)
#pragma alloc_text(PAGE, RxRecurrentTimerWorkItemDispatcher)
#endif
typedef struct _RX_RECURRENT_WORK_ITEM_ { RX_WORK_ITEM WorkItem; LIST_ENTRY RecurrentWorkItemsList; LARGE_INTEGER TimeInterval; PRX_WORKERTHREAD_ROUTINE Routine; PVOID pContext; } RX_RECURRENT_WORK_ITEM, *PRX_RECURRENT_WORK_ITEM;
//
// Forward declarations of routines
//
extern VOID RxTimerDispatch( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 );
extern VOID RxRecurrentTimerWorkItemDispatcher ( IN PVOID Context );
// The Bug check file id for this module
#define BugCheckFileId (RDBSS_BUG_CHECK_NTTIMER)
// The local trace mask for this part of the module
#define Dbg (DEBUG_TRACE_NTTIMER)
LARGE_INTEGER s_RxTimerInterval; KSPIN_LOCK s_RxTimerLock; KDPC s_RxTimerDpc; LIST_ENTRY s_RxTimerQueueHead; // queue of the list of timer calls
LIST_ENTRY s_RxRecurrentWorkItemsList; KTIMER s_RxTimer; ULONG s_RxTimerTickCount;
#define NoOf100nsTicksIn1ms (10 * 1000)
#define NoOf100nsTicksIn55ms (10 * 1000 * 55)
NTSTATUS RxInitializeRxTimer() /*++
Routine Description:
The routine initializes everything having to do with the timer stuff.
Arguments:
none
Return Value:
none
--*/ { NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
s_RxTimerInterval.LowPart = (ULONG)(-((LONG)NoOf100nsTicksIn55ms)); s_RxTimerInterval.HighPart = -1;
KeInitializeSpinLock( &s_RxTimerLock );
InitializeListHead( &s_RxTimerQueueHead ); InitializeListHead( &s_RxRecurrentWorkItemsList );
KeInitializeDpc( &s_RxTimerDpc, RxTimerDispatch, NULL ); KeInitializeTimer( &s_RxTimer );
s_RxTimerTickCount = 0;
return Status; }
VOID RxTearDownRxTimer( void) /*++
Routine Description:
This routine is used by drivers to initialize a timer entry for a device object.
Arguments:
TimerEntry - Pointer to a timer entry to be used.
TimerRoutine - Driver routine to be executed when timer expires.
Context - Context parameter that is passed to the driver routine.
Return Value:
The function value indicates whether or not the timer was initialized.
--*/
{ PRX_RECURRENT_WORK_ITEM pWorkItem; PLIST_ENTRY pListEntry;
PAGED_CODE();
KeCancelTimer( &s_RxTimer );
// Walk down the list freeing up the recurrent requests since the memory was
// allocated by us.
while (!IsListEmpty(&s_RxRecurrentWorkItemsList)) { pListEntry = RemoveHeadList(&s_RxRecurrentWorkItemsList); pWorkItem = (PRX_RECURRENT_WORK_ITEM) CONTAINING_RECORD( pListEntry, RX_RECURRENT_WORK_ITEM, RecurrentWorkItemsList); RxFreePool(pWorkItem); } }
VOID RxTimerDispatch( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++
Routine Description:
This routine scans the timer database and posts a work item for all those requests whose temporal constraints have been satisfied.
Arguments:
Dpc - Supplies a pointer to a control object of type DPC.
DeferredContext - Optional deferred context; not used.
SystemArgument1 - Optional argument 1; not used.
SystemArgument2 - Optional argument 2; not used.
Return Value:
None.
--*/ { PLIST_ENTRY pListEntry; LIST_ENTRY ExpiredList;
//KIRQL Irql;
BOOLEAN ContinueTimer = FALSE;
PRX_WORK_QUEUE_ITEM pWorkQueueItem; PRX_WORK_ITEM pWorkItem;
UNREFERENCED_PARAMETER( Dpc ); UNREFERENCED_PARAMETER( DeferredContext ); UNREFERENCED_PARAMETER( SystemArgument1 ); UNREFERENCED_PARAMETER( SystemArgument2 );
InitializeListHead(&ExpiredList);
KeAcquireSpinLockAtDpcLevel( &s_RxTimerLock );
s_RxTimerTickCount++;
pListEntry = s_RxTimerQueueHead.Flink;
while (pListEntry != &s_RxTimerQueueHead) { pWorkQueueItem = CONTAINING_RECORD( pListEntry, RX_WORK_QUEUE_ITEM, List ); pWorkItem = CONTAINING_RECORD( pWorkQueueItem, RX_WORK_ITEM, WorkQueueItem);
if (pWorkItem->LastTick == s_RxTimerTickCount) { PLIST_ENTRY pExpiredEntry = pListEntry; pListEntry = pListEntry->Flink;
RemoveEntryList(pExpiredEntry); InsertTailList(&ExpiredList,pExpiredEntry); } else { pListEntry = pListEntry->Flink; } }
ContinueTimer = !(IsListEmpty(&s_RxTimerQueueHead)); KeReleaseSpinLockFromDpcLevel( &s_RxTimerLock );
// Resubmit the timer queue dispatch routine so that it will be reinvoked.
if (ContinueTimer) KeSetTimer( &s_RxTimer, s_RxTimerInterval, &s_RxTimerDpc );
// Queue all the expired entries on the worker threads.
while (!IsListEmpty(&ExpiredList)) { pListEntry = RemoveHeadList(&ExpiredList); pListEntry->Flink = pListEntry->Blink = NULL;
pWorkQueueItem = CONTAINING_RECORD( pListEntry, RX_WORK_QUEUE_ITEM, List );
// Post the work item to a worker thread
RxPostToWorkerThread( pWorkQueueItem->pDeviceObject, CriticalWorkQueue, pWorkQueueItem, pWorkQueueItem->WorkerRoutine, pWorkQueueItem->Parameter); } }
NTSTATUS RxPostOneShotTimerRequest( IN PRDBSS_DEVICE_OBJECT pDeviceObject, IN PRX_WORK_ITEM pWorkItem, IN PRX_WORKERTHREAD_ROUTINE Routine, IN PVOID pContext, IN LARGE_INTEGER TimeInterval) /*++
Routine Description:
This routine is used by drivers to initialize a timer entry for a device object.
Arguments:
pDeviceObject - the device object
pWorkItem - the work item
Routine - the routine to be invoked on timeout
pContext - the Context parameter that is passed to the driver routine.
TimeInterval - the time interval in 100 ns ticks.
Return Value:
The function value indicates whether or not the timer was initialized.
--*/
{ BOOLEAN StartTimer; //NTSTATUS Status;
ULONG NumberOf55msIntervals; KIRQL Irql; LARGE_INTEGER StrobeInterval;
ASSERT(pWorkItem != NULL);
// Initialize the work queue item.
ExInitializeWorkItem( (PWORK_QUEUE_ITEM)&pWorkItem->WorkQueueItem, Routine, pContext );
pWorkItem->WorkQueueItem.pDeviceObject = pDeviceObject;
// Compute the time interval in number of ticks.
StrobeInterval.QuadPart= NoOf100nsTicksIn55ms; NumberOf55msIntervals = (ULONG)(TimeInterval.QuadPart / StrobeInterval.QuadPart); NumberOf55msIntervals += 1; // Take the ceiling to be conservative
RxDbgTraceLV( 0, Dbg, 1500, ("Timer will expire after %ld 55ms intervals\n",NumberOf55msIntervals));
// Insert the entry in the timer queue.
KeAcquireSpinLock( &s_RxTimerLock, &Irql );
// Update the tick relative to the current tick.
pWorkItem->LastTick = s_RxTimerTickCount + NumberOf55msIntervals;
StartTimer = IsListEmpty(&s_RxTimerQueueHead); InsertTailList( &s_RxTimerQueueHead,&pWorkItem->WorkQueueItem.List);
KeReleaseSpinLock( &s_RxTimerLock, Irql );
if (StartTimer) { KeSetTimer( &s_RxTimer, s_RxTimerInterval, &s_RxTimerDpc ); }
return STATUS_SUCCESS; }
NTSTATUS RxPostRecurrentTimerRequest( IN PRDBSS_DEVICE_OBJECT pDeviceObject, IN PRX_WORKERTHREAD_ROUTINE Routine, IN PVOID pContext, IN LARGE_INTEGER TimeInterval) /*++
Routine Description:
This routine is used to post a recurrent timer request. The passed in routine once every (TimeInterval) milli seconds.
Arguments:
pDeviceObject - the device object
Routine - the routine to be invoked on timeout
pContext - the Context parameter that is passed to the driver routine.
TimeInterval - the time interval in 100ns ticks.
Return Value:
The function value indicates whether or not the timer was initialized.
--*/ { PRX_RECURRENT_WORK_ITEM pRecurrentWorkItem; NTSTATUS Status;
PAGED_CODE();
// Allocate a work item.
pRecurrentWorkItem = (PRX_RECURRENT_WORK_ITEM) RxAllocatePoolWithTag( NonPagedPool, sizeof(RX_RECURRENT_WORK_ITEM), RX_TIMER_POOLTAG);
if (pRecurrentWorkItem != NULL) { InsertTailList( &s_RxRecurrentWorkItemsList, &pRecurrentWorkItem->RecurrentWorkItemsList);
pRecurrentWorkItem->Routine = Routine; pRecurrentWorkItem->pContext = pContext; pRecurrentWorkItem->TimeInterval = TimeInterval; pRecurrentWorkItem->WorkItem.WorkQueueItem.pDeviceObject = pDeviceObject;
Status = RxPostOneShotTimerRequest( pRecurrentWorkItem->WorkItem.WorkQueueItem.pDeviceObject, &pRecurrentWorkItem->WorkItem, RxRecurrentTimerWorkItemDispatcher, pRecurrentWorkItem, TimeInterval); } else { Status = STATUS_INSUFFICIENT_RESOURCES; }
return Status; }
NTSTATUS RxCancelTimerRequest( IN PRDBSS_DEVICE_OBJECT pDeviceObject, IN PRX_WORKERTHREAD_ROUTINE Routine, IN PVOID pContext) /*++
Routine Description:
This routine cancels a timer request. The request to be cancelled is identified by the routine and context.
Arguments:
Routine - the routine to be invoked on timeout
pContext - the Context parameter that is passed to the driver routine.
--*/ { NTSTATUS Status = STATUS_NOT_FOUND; PLIST_ENTRY pListEntry; PWORK_QUEUE_ITEM pWorkQueueItem; PRX_WORK_ITEM pWorkItem; PRX_RECURRENT_WORK_ITEM pRecurrentWorkItem = NULL; KIRQL Irql;
KeAcquireSpinLock( &s_RxTimerLock, &Irql );
// Walk through the list of entries
for (pListEntry = s_RxTimerQueueHead.Flink; (pListEntry != &s_RxTimerQueueHead); pListEntry = pListEntry->Flink ) { pWorkQueueItem = CONTAINING_RECORD( pListEntry, WORK_QUEUE_ITEM, List ); pWorkItem = CONTAINING_RECORD( pWorkQueueItem, RX_WORK_ITEM, WorkQueueItem);
if ((pWorkItem->WorkQueueItem.pDeviceObject == pDeviceObject) && (pWorkItem->WorkQueueItem.WorkerRoutine == Routine) && (pWorkItem->WorkQueueItem.Parameter == pContext)) { RemoveEntryList(pListEntry); Status = STATUS_SUCCESS; pRecurrentWorkItem = NULL; break; } else if (pWorkItem->WorkQueueItem.WorkerRoutine == RxRecurrentTimerWorkItemDispatcher) { pRecurrentWorkItem = (PRX_RECURRENT_WORK_ITEM)pWorkItem->WorkQueueItem.Parameter;
if ((pRecurrentWorkItem->Routine == Routine) && (pRecurrentWorkItem->pContext == pContext)) { RemoveEntryList(pListEntry); RemoveEntryList(&pRecurrentWorkItem->RecurrentWorkItemsList); Status = STATUS_SUCCESS; } else { pRecurrentWorkItem = NULL; } } }
KeReleaseSpinLock( &s_RxTimerLock, Irql );
if (pRecurrentWorkItem != NULL) { RxFreePool(pRecurrentWorkItem); }
return Status; }
VOID RxRecurrentTimerWorkItemDispatcher ( IN PVOID Context ) /*++
Routine Description:
This routine dispatches a recurrent timer request. On completion of the invocation of the associated routine the request s requeued.
Arguments:
Routine - the routine to be invoked on timeout
pContext - the Context parameter that is passed to the driver routine.
--*/ { PRX_RECURRENT_WORK_ITEM pPeriodicWorkItem = (PRX_RECURRENT_WORK_ITEM)Context; PRX_WORKERTHREAD_ROUTINE Routine = pPeriodicWorkItem->Routine; PVOID pContext = pPeriodicWorkItem->pContext;
PAGED_CODE();
//KIRQL Irql;
// Invoke the routine.
Routine(pContext);
// enqueue the item if necessary.
RxPostOneShotTimerRequest( pPeriodicWorkItem->WorkItem.WorkQueueItem.pDeviceObject, &pPeriodicWorkItem->WorkItem, RxRecurrentTimerWorkItemDispatcher, pPeriodicWorkItem, pPeriodicWorkItem->TimeInterval); }
|