|
|
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
timerobj.c
Abstract:
This module implements the kernel timer object. Functions are provided to initialize, read, set, and cancel timer objects.
Author:
David N. Cutler (davec) 2-Mar-1989
Environment:
Kernel mode only.
Revision History:
--*/
#include "ki.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGELK, KeQueryTimerDueTime)
#endif
//
// The following assert macro is used to check that an input timer is
// really a ktimer and not something else, like deallocated pool.
//
#define ASSERT_TIMER(E) { \
ASSERT(((E)->Header.Type == TimerNotificationObject) || \ ((E)->Header.Type == TimerSynchronizationObject)); \ }
VOID KeInitializeTimer ( IN PKTIMER Timer )
/*++
Routine Description:
This function initializes a kernel timer object.
Arguments:
Timer - Supplies a pointer to a dispatcher object of type timer.
Return Value:
None.
--*/
{
//
// Initialize extended timer object with a type of notification and a
// period of zero.
//
KeInitializeTimerEx(Timer, NotificationTimer); return; }
VOID KeInitializeTimerEx ( IN PKTIMER Timer, IN TIMER_TYPE Type )
/*++
Routine Description:
This function initializes an extended kernel timer object.
Arguments:
Timer - Supplies a pointer to a dispatcher object of type timer.
Type - Supplies the type of timer object; NotificationTimer or SynchronizationTimer;
Return Value:
None.
--*/
{ //
// Initialize standard dispatcher object header and set initial
// state of timer.
//
Timer->Header.Type = (UCHAR)(TimerNotificationObject + Type); Timer->Header.Inserted = FALSE; Timer->Header.Size = sizeof(KTIMER) / sizeof(LONG); Timer->Header.SignalState = FALSE;
#if DBG
Timer->TimerListEntry.Flink = NULL; Timer->TimerListEntry.Blink = NULL;
#endif
InitializeListHead(&Timer->Header.WaitListHead); Timer->DueTime.QuadPart = 0; Timer->Period = 0; return; }
VOID KeClearTimer ( IN PKTIMER Timer )
/*++
Routine Description:
This function clears the signal state of an timer object.
Arguments:
Event - Supplies a pointer to a dispatcher object of type timer.
Return Value:
None.
--*/
{
ASSERT_TIMER(Timer);
//
// Clear signal state of timer object.
//
Timer->Header.SignalState = 0; return; }
BOOLEAN KeCancelTimer ( IN PKTIMER Timer )
/*++
Routine Description:
This function cancels a timer that was previously set to expire at a specified time. If the timer is not currently set, then no operation is performed. Canceling a timer does not set the state of the timer to Signaled.
Arguments:
Timer - Supplies a pointer to a dispatcher object of type timer.
Return Value:
A boolean value of TRUE is returned if the the specified timer was currently set. Else a value of FALSE is returned.
--*/
{
BOOLEAN Inserted; KIRQL OldIrql;
ASSERT_TIMER(Timer); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level, lock the dispatcher database, and
// capture the timer inserted status. If the timer is currently set,
// then remove it from the timer list.
//
KiLockDispatcherDatabase(&OldIrql); Inserted = Timer->Header.Inserted; if (Inserted != FALSE) { KiRemoveTreeTimer(Timer); }
//
// Unlock the dispatcher database, lower IRQL to its previous value, and
// return boolean value that signifies whether the timer was set of not.
//
KiUnlockDispatcherDatabase(OldIrql); return Inserted; }
BOOLEAN KeReadStateTimer ( IN PKTIMER Timer )
/*++
Routine Description:
This function reads the current signal state of a timer object.
Arguments:
Timer - Supplies a pointer to a dispatcher object of type timer.
Return Value:
The current signal state of the timer object.
--*/
{
ASSERT_TIMER(Timer);
//
// Return current signal state of timer object.
//
return (BOOLEAN)Timer->Header.SignalState; }
BOOLEAN KeSetTimer ( IN PKTIMER Timer, IN LARGE_INTEGER DueTime, IN PKDPC Dpc OPTIONAL )
/*++
Routine Description:
This function sets a timer to expire at a specified time. If the timer is already set, then it is implicitly canceled before it is set to expire at the specified time. Setting a timer causes its due time to be computed, its state to be set to Not-Signaled, and the timer object itself to be inserted in the timer list.
Arguments:
Timer - Supplies a pointer to a dispatcher object of type timer.
DueTime - Supplies an absolute or relative time at which the timer is to expire.
Dpc - Supplies an optional pointer to a control object of type DPC.
Return Value:
A boolean value of TRUE is returned if the the specified timer was currently set. Else a value of FALSE is returned.
--*/
{
//
// Set the timer with a period of zero.
//
return KeSetTimerEx(Timer, DueTime, 0, Dpc); }
BOOLEAN KeSetTimerEx ( IN PKTIMER Timer, IN LARGE_INTEGER DueTime, IN LONG Period OPTIONAL, IN PKDPC Dpc OPTIONAL )
/*++
Routine Description:
This function sets a timer to expire at a specified time. If the timer is already set, then it is implicitly canceled before it is set to expire at the specified time. Setting a timer causes its due time to be computed, its state to be set to Not-Signaled, and the timer object itself to be inserted in the timer list.
Arguments:
Timer - Supplies a pointer to a dispatcher object of type timer.
DueTime - Supplies an absolute or relative time at which the timer is to expire.
Period - Supplies an optional period for the timer in milliseconds.
Dpc - Supplies an optional pointer to a control object of type DPC.
Return Value:
A boolean value of TRUE is returned if the the specified timer was currently set. Else a value of FALSE is returned.
--*/
{
BOOLEAN Inserted; LARGE_INTEGER Interval; KIRQL OldIrql; LARGE_INTEGER SystemTime;
ASSERT_TIMER(Timer); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
KiLockDispatcherDatabase(&OldIrql);
//
// Capture the timer inserted status and if the timer is currently
// set, then remove it from the timer list.
//
Inserted = Timer->Header.Inserted; if (Inserted != FALSE) { KiRemoveTreeTimer(Timer); }
//
// Clear the signal state, set the period, set the DPC address, and
// insert the timer in the timer tree. If the timer is not inserted
// in the timer tree, then it has already expired and as many waiters
// as possible should be continued, and a DPC, if specified should be
// queued.
//
// N.B. The signal state must be cleared in case the period is not
// zero.
//
Timer->Header.SignalState = FALSE; Timer->Dpc = Dpc; Timer->Period = Period; if (KiInsertTreeTimer((PRKTIMER)Timer, DueTime) == FALSE) { if (IsListEmpty(&Timer->Header.WaitListHead) == FALSE) { KiWaitTest(Timer, TIMER_EXPIRE_INCREMENT); }
//
// If a DPC is specfied, then call the DPC routine.
//
if (Dpc != NULL) { KiQuerySystemTime(&SystemTime); KeInsertQueueDpc(Timer->Dpc, ULongToPtr(SystemTime.LowPart), ULongToPtr(SystemTime.HighPart)); }
//
// If the timer is periodic, then compute the next interval time
// and reinsert the timer in the timer tree.
//
// N.B. Even though the timer insertion is relative, it can still
// fail if the period of the timer elapses in between computing
// the time and inserting the timer. If this happens, then the
// the insertion is retried.
//
if (Period != 0) { Interval.QuadPart = Int32x32To64(Timer->Period, - 10 * 1000); do { } while (KiInsertTreeTimer(Timer, Interval) == FALSE); } }
//
// Unlock the dispatcher database and lower IRQL to its previous
// value.
//
KiUnlockDispatcherDatabase(OldIrql);
//
// Return boolean value that signifies whether the timer was set of
// not.
//
return Inserted; }
ULONGLONG KeQueryTimerDueTime ( IN PKTIMER Timer )
/*++
Routine Description:
This function returns the InterruptTime at which the timer is pending. 0 is returned if the timer is not pending.
N.B. This function may only be called by the system sleep code.
Arguments:
Timer - Supplies a pointer to a dispatcher object of type timer.
Return Value:
Returns the amount of time remaining on the timer, or 0 if the timer is not pending.
--*/
{
KIRQL OldIrql; ULONGLONG DueTime;
ASSERT_TIMER(Timer);
DueTime = 0;
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
KiLockDispatcherDatabase(&OldIrql);
//
// If the timer is currently pending, compute its due time
//
if (Timer->Header.Inserted) { DueTime = Timer->DueTime.QuadPart; }
//
// Unlock the dispatcher database and lower IRQL to its previous
// value, and return the due time
//
KiUnlockDispatcherDatabase(OldIrql); return DueTime; }
VOID KeCheckForTimer( IN PVOID BlockStart, IN SIZE_T BlockSize ) /*++
Routine Description:
This function is used for debugging by checking all timers to see if any is in the memory block passed. If so, the system bugchecks.
Arguments:
BlockStart - Base address to check for timer.
BlockSize - Size (in bytes) to check in the memory block.
Return Value:
None.
--*/ { ULONG Index; PLIST_ENTRY ListHead; PLIST_ENTRY NextEntry; KIRQL OldIrql; PKTIMER Timer; PUCHAR Address; PUCHAR Start; PUCHAR End;
//
// Compute the ending memory location.
//
Start = (PUCHAR)BlockStart; End = Start + BlockSize;
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
KiLockDispatcherDatabase(&OldIrql);
//
// Run the entire timer database and check for any timers in
// the memory block
//
Index = 0; do { ListHead = &KiTimerTableListHead[Index]; NextEntry = ListHead->Flink; while (NextEntry != ListHead) { Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); Address = (PUCHAR)Timer; NextEntry = NextEntry->Flink;
//
// Check this timer object is not in the range.
// In each of the following, we check that the object
// does not overlap the range, for example, if the timer
// object (in this first check), starts one dword before
// the range being checked, we have an overlap and should
// stop.
//
if ((Address > (Start - sizeof(KTIMER))) && (Address < End)) { KeBugCheckEx(TIMER_OR_DPC_INVALID, 0x0, (ULONG_PTR)Address, (ULONG_PTR)Start, (ULONG_PTR)End); }
if (Timer->Dpc) {
//
// Check the timer's DPC object isn't in the range.
//
Address = (PUCHAR)Timer->Dpc; if ((Address > (Start - sizeof(KDPC))) && (Address < End)) { KeBugCheckEx(TIMER_OR_DPC_INVALID, 0x1, (ULONG_PTR)Address, (ULONG_PTR)Start, (ULONG_PTR)End); }
//
// Check the timer's DPC routine is not in the range.
//
Address = (PUCHAR)(ULONG_PTR) Timer->Dpc->DeferredRoutine; if (Address >= Start && Address < End) { KeBugCheckEx(TIMER_OR_DPC_INVALID, 0x2, (ULONG_PTR)Address, (ULONG_PTR)Start, (ULONG_PTR)End); } } }
Index += 1; } while(Index < TIMER_TABLE_SIZE);
//
// Unlock the dispatcher database and lower IRQL to its previous value
//
KiUnlockDispatcherDatabase(OldIrql); }
|