ULONGLONG KeQueryInterruptTime ( VOID ) /*++ Routine Description: This function returns the current interrupt time by determining when the time is stable and then returning its value. Arguments: CurrentTime - Supplies a pointer to a variable that will receive the current system time. Return Value: None. --*/ { LARGE_INTEGER CurrentTime; KiQueryInterruptTime(&CurrentTime); return CurrentTime.QuadPart; }
PLARGE_INTEGER FASTCALL KiComputeWaitInterval ( IN PLARGE_INTEGER OriginalTime, IN PLARGE_INTEGER DueTime, IN OUT PLARGE_INTEGER NewTime ) /*++ Routine Description: This function recomputes the wait interval after a thread has been awakened to deliver a kernel APC. Arguments: OriginalTime - Supplies a pointer to the original timeout value. DueTime - Supplies a pointer to the previous due time. NewTime - Supplies a pointer to a variable that receives the recomputed wait interval. Return Value: A pointer to the new time is returned as the function value. --*/ { // // If the original wait time was absolute, then return the same // absolute time. Otherwise, reduce the wait time remaining before // the time delay expires. // if (OriginalTime->QuadPart >= 0) { return OriginalTime; } else { KiQueryInterruptTime(NewTime); NewTime->QuadPart -= DueTime->QuadPart; return NewTime; } }
NTSTATUS NtQueryTimer ( IN HANDLE TimerHandle, IN TIMER_INFORMATION_CLASS TimerInformationClass, OUT PVOID TimerInformation, IN ULONG TimerInformationLength, OUT PULONG ReturnLength OPTIONAL ) /*++ Routine Description: This function queries the state of an timer object and returns the requested information in the specified record structure. Arguments: TimerHandle - Supplies a handle to an timer object. TimerInformationClass - Supplies the class of information being requested. TimerInformation - Supplies a pointer to a record that is to receive the requested information. TimerInformationLength - Supplies the length of the record that is to receive the requested information. ReturnLength - Supplies an optional pointer to a variable that is to receive the actual length of information that is returned. Return Value: TBS --*/ { PETIMER ExTimer; PKTIMER KeTimer; KPROCESSOR_MODE PreviousMode; BOOLEAN State; NTSTATUS Status; LARGE_INTEGER TimeToGo; // // Establish an exception handler, probe the output arguments, reference // the timer object, and return the specified information. If the probe // fails, then return the exception code as the service status. Otherwise // return the status value returned by the reference object by handle // routine. // try { // // Get previous processor mode and probe output arguments if necessary. // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { ProbeForWrite(TimerInformation, sizeof(TIMER_BASIC_INFORMATION), sizeof(ULONG)); if (ARGUMENT_PRESENT(ReturnLength)) { ProbeForWriteUlong(ReturnLength); } } // // Check argument validity. // if (TimerInformationClass != TimerBasicInformation) { return STATUS_INVALID_INFO_CLASS; } if (TimerInformationLength != sizeof(TIMER_BASIC_INFORMATION)) { return STATUS_INFO_LENGTH_MISMATCH; } // // Reference timer object by handle. // Status = ObReferenceObjectByHandle(TimerHandle, TIMER_QUERY_STATE, ExTimerObjectType, PreviousMode, (PVOID *)&ExTimer, NULL); // // If the reference was successful, then read the current state, // compute the time remaining, dereference the timer object, fill in // the information structure, and return the length of the information // structure if specified. If the write of the time information or the // return length fails, then do not report an error. When the caller // accesses the information structure or the length, an violation will // occur. // if (NT_SUCCESS(Status)) { KeTimer = &ExTimer->KeTimer; State = KeReadStateTimer(KeTimer); KiQueryInterruptTime(&TimeToGo); TimeToGo.QuadPart = KeTimer->DueTime.QuadPart - TimeToGo.QuadPart; ObDereferenceObject(ExTimer); try { ((PTIMER_BASIC_INFORMATION)TimerInformation)->TimerState = State; ((PTIMER_BASIC_INFORMATION)TimerInformation)->RemainingTime = TimeToGo; if (ARGUMENT_PRESENT(ReturnLength)) { *ReturnLength = sizeof(TIMER_BASIC_INFORMATION); } } except(ExSystemExceptionFilter()) { } } // // If an exception occurs during the probe of the current state address, // then always handle the exception and return the exception code as the // status value. // } except(ExSystemExceptionFilter()) { return GetExceptionCode(); } // // Return service status. // return Status; }
VOID KiTimerExpiration ( IN PKDPC TimerDpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++ Routine Description: This function is called when the clock interupt routine discovers that a timer has expired. Arguments: TimerDpc - Supplies a pointer to a control object of type DPC. DeferredContext - Not used. SystemArgument1 - Supplies the starting timer table index value to use for the timer table scan. SystemArgument2 - Not used. Return Value: None. --*/ { ULARGE_INTEGER CurrentTime; LIST_ENTRY ExpiredListHead; LONG HandLimit; LONG Index; PLIST_ENTRY ListHead; PLIST_ENTRY NextEntry; KIRQL OldIrql; PKTIMER Timer; // // Acquire the dispatcher database lock and read the current interrupt // time to determine which timers have expired. // KiLockDispatcherDatabase(&OldIrql); KiQueryInterruptTime((PLARGE_INTEGER)&CurrentTime); // // If the timer table has not wrapped, then start with the specified // timer table index value, and scan for timer entries that have expired. // Otherwise, start with the first entry in the timer table and scan the // entire table for timer entries that have expired. // // N.B. This later condition exists when DPC processing is blocked for a // period longer than one round trip throught the timer table. // HandLimit = (LONG)KiQueryLowTickCount(); if (((ULONG)(HandLimit - PtrToLong(SystemArgument1))) >= TIMER_TABLE_SIZE) { Index = - 1; HandLimit = TIMER_TABLE_SIZE - 1; } else { Index = (PtrToLong(SystemArgument1) - 1) & (TIMER_TABLE_SIZE - 1); HandLimit &= (TIMER_TABLE_SIZE - 1); } InitializeListHead(&ExpiredListHead); do { Index = (Index + 1) & (TIMER_TABLE_SIZE - 1); ListHead = &KiTimerTableListHead[Index]; NextEntry = ListHead->Flink; while (NextEntry != ListHead) { Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); if (Timer->DueTime.QuadPart <= CurrentTime.QuadPart) { // // The next timer in the current timer list has expired. // Remove the entry from the timer list and insert the // timer in the expired list. // RemoveEntryList(&Timer->TimerListEntry); InsertTailList(&ExpiredListHead, &Timer->TimerListEntry); NextEntry = ListHead->Flink; } else { break; } } } while(Index != HandLimit); #if DBG if ((PtrToUlong(SystemArgument2) == 0) && (KeNumberProcessors == 1)) { KiCheckTimerTable(CurrentTime); } #endif // // Process the expired timer list. // // N.B. The following function returns with the dispatcher database // unlocked. // KiTimerListExpire(&ExpiredListHead, OldIrql); return; }
LOGICAL FASTCALL KiInsertTreeTimer ( IN PRKTIMER Timer, IN LARGE_INTEGER Interval ) /*++ Routine Description: This function inserts a timer object in the timer queue. N.B. This routine assumes that the dispatcher data lock has been acquired. Arguments: Timer - Supplies a pointer to a dispatcher object of type timer. Interval - Supplies the absolute or relative time at which the time is to expire. Return Value: If the timer is inserted in the timer tree, than a value of TRUE is returned. Otherwise, a value of FALSE is returned. --*/ { LARGE_INTEGER CurrentTime; LARGE_INTEGER SystemTime; LARGE_INTEGER TimeDifference; // // Clear the signal state of timer if the timer period is zero and set // the inserted state to TRUE. // Timer->Header.Inserted = TRUE; Timer->Header.Absolute = FALSE; if (Timer->Period == 0) { Timer->Header.SignalState = FALSE; } // // If the specified interval is not a relative time (i.e., is an absolute // time), then convert it to relative time. // if (Interval.HighPart >= 0) { KiQuerySystemTime(&SystemTime); TimeDifference.QuadPart = SystemTime.QuadPart - Interval.QuadPart; // // If the resultant relative time is greater than or equal to zero, // then the timer has already expired. // if (TimeDifference.HighPart >= 0) { Timer->Header.SignalState = TRUE; Timer->Header.Inserted = FALSE; return FALSE; } Interval = TimeDifference; Timer->Header.Absolute = TRUE; } // // Get the current interrupt time, insert the timer in the timer table, // and return the inserted state. // KiQueryInterruptTime(&CurrentTime); return KiInsertTimerTable(Interval, CurrentTime, Timer); }
LOGICAL FASTCALL KiInsertTimerTable ( LARGE_INTEGER Interval, LARGE_INTEGER CurrentTime, IN PRKTIMER Timer ) /*++ Routine Description: This function inserts a timer object in the timer table. N.B. This routine assumes that the dispatcher data lock has been acquired. Arguments: Interval - Supplies the relative timer before the timer is to expire. CurrentTime - supplies the current interrupt time. Timer - Supplies a pointer to a dispatcher object of type timer. Return Value: If the timer is inserted in the timer tree, than a value of TRUE is returned. Otherwise, a value of FALSE is returned. --*/ { ULONG Index; PLIST_ENTRY ListHead; PLIST_ENTRY NextEntry; PRKTIMER NextTimer; ULONG SearchCount; // // Compute the timer table index and set the timer expiration time. // Index = KiComputeTimerTableIndex(Interval, CurrentTime, Timer); // // If the timer is due before the first entry in the computed list // or the computed list is empty, then insert the timer at the front // of the list and check if the timer has already expired. Otherwise, // insert then timer in the sorted order of the list searching from // the back of the list forward. // // N.B. The sequence of operations below is critical to avoid the race // condition that exists between this code and the clock interrupt // code that examines the timer table lists to detemine when timers // expire. // ListHead = &KiTimerTableListHead[Index]; NextEntry = ListHead->Blink; #if DBG SearchCount = 0; #endif while (NextEntry != ListHead) { // // Compute the maximum search count. // #if DBG SearchCount += 1; if (SearchCount > KiMaximumSearchCount) { KiMaximumSearchCount = SearchCount; } #endif NextTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); if (((Timer->DueTime.HighPart == NextTimer->DueTime.HighPart) && (Timer->DueTime.LowPart >= NextTimer->DueTime.LowPart)) || (Timer->DueTime.HighPart > NextTimer->DueTime.HighPart)) { InsertHeadList(NextEntry, &Timer->TimerListEntry); return TRUE; } NextEntry = NextEntry->Blink; } // // The computed list is empty or the timer is due to expire before // the first entry in the list. Insert the entry in the computed // timer table list, then check if the timer has expired. // // Note that it is critical that the interrupt time not be captured // until after the timer has been completely inserted into the list. // // Otherwise, the clock interrupt code can think the list is empty, // and the code here that checks if the timer has expired will use // a stale interrupt time. // InsertHeadList(ListHead, &Timer->TimerListEntry); KiQueryInterruptTime(&CurrentTime); if (((Timer->DueTime.HighPart == (ULONG)CurrentTime.HighPart) && (Timer->DueTime.LowPart <= CurrentTime.LowPart)) || (Timer->DueTime.HighPart < (ULONG)CurrentTime.HighPart)) { // // The timer is due to expire before the current time. Remove the // timer from the computed list, set its status to Signaled, set // its inserted state to FALSE, and // KiRemoveTreeTimer(Timer); Timer->Header.SignalState = TRUE; } return Timer->Header.Inserted; }
LOGICAL FASTCALL KiReinsertTreeTimer ( IN PRKTIMER Timer, IN ULARGE_INTEGER DueTime ) /*++ Routine Description: This function reinserts a timer object in the timer queue. N.B. This routine assumes that the dispatcher data lock has been acquired. Arguments: Timer - Supplies a pointer to a dispatcher object of type timer. DueTime - Supplies the absolute time the timer is to expire. Return Value: If the timer is inserted in the timer tree, than a value of TRUE is returned. Otherwise, a value of FALSE is returned. --*/ { LARGE_INTEGER CurrentTime; LARGE_INTEGER Interval; // // Clear the signal state of timer if the timer period is zero and set // the inserted state to TRUE. // Timer->Header.Inserted = TRUE; if (Timer->Period == 0) { Timer->Header.SignalState = FALSE; } // // Compute the interval between the current time and the due time. // If the resultant relative time is greater than or equal to zero, // then the timer has already expired. // KiQueryInterruptTime(&CurrentTime); Interval.QuadPart = CurrentTime.QuadPart - DueTime.QuadPart; if (Interval.QuadPart >= 0) { Timer->Header.SignalState = TRUE; Timer->Header.Inserted = FALSE; return FALSE; } // // Insert the timer in the timer table and return the inserted state. // return KiInsertTimerTable(Interval, CurrentTime, Timer); }