Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344/* * PROJECT: ReactOS Kernel * LICENSE: GPL - See COPYING in the top level directory * FILE: ntoskrnl/ke/timerobj.c * PURPOSE: Handle Kernel Timers (Kernel-part of Executive Timers) * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) */
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>#define NDEBUG#include <debug.h>
/* GLOBALS *******************************************************************/
KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE];LARGE_INTEGER KiTimeIncrementReciprocal;UCHAR KiTimeIncrementShiftCount;BOOLEAN KiEnableTimerWatchdog = FALSE;
/* PRIVATE FUNCTIONS *********************************************************/
BOOLEANFASTCALLKiInsertTreeTimer(IN PKTIMER Timer, IN LARGE_INTEGER Interval){ BOOLEAN Inserted = FALSE; ULONG Hand = 0; PKSPIN_LOCK_QUEUE LockQueue; DPRINT("KiInsertTreeTimer(): Timer %p, Interval: %I64d\n", Timer, Interval.QuadPart);
/* Setup the timer's due time */ if (KiComputeDueTime(Timer, Interval, &Hand)) { /* Acquire the lock */ LockQueue = KiAcquireTimerLock(Hand);
/* Insert the timer */ if (KiInsertTimerTable(Timer, Hand)) { /* It was already there, remove it */ KiRemoveEntryTimer(Timer); Timer->Header.Inserted = FALSE; } else { /* Otherwise, we're now inserted */ Inserted = TRUE; }
/* Release the lock */ KiReleaseTimerLock(LockQueue); }
/* Release the lock and return insert status */ return Inserted;}
BOOLEANFASTCALLKiInsertTimerTable(IN PKTIMER Timer, IN ULONG Hand){ ULONGLONG InterruptTime; ULONGLONG DueTime = Timer->DueTime.QuadPart; BOOLEAN Expired = FALSE; PLIST_ENTRY ListHead, NextEntry; PKTIMER CurrentTimer; DPRINT("KiInsertTimerTable(): Timer %p, Hand: %lu\n", Timer, Hand);
/* Check if the period is zero */ if (!Timer->Period) Timer->Header.SignalState = FALSE;
/* Sanity check */ ASSERT(Hand == KiComputeTimerTableIndex(DueTime));
/* Loop the timer list backwards */ ListHead = &KiTimerTableListHead[Hand].Entry; NextEntry = ListHead->Blink; while (NextEntry != ListHead) { /* Get the timer */ CurrentTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
/* Now check if we can fit it before */ if ((ULONGLONG)DueTime >= CurrentTimer->DueTime.QuadPart) break;
/* Keep looping */ NextEntry = NextEntry->Blink; }
/* Looped all the list, insert it here and get the interrupt time again */ InsertHeadList(NextEntry, &Timer->TimerListEntry);
/* Check if we didn't find it in the list */ if (NextEntry == ListHead) { /* Set the time */ KiTimerTableListHead[Hand].Time.QuadPart = DueTime;
/* Make sure it hasn't expired already */ InterruptTime = KeQueryInterruptTime(); if (DueTime <= InterruptTime) Expired = TRUE; }
/* Return expired state */ return Expired;}
BOOLEANFASTCALLKiSignalTimer(IN PKTIMER Timer){ BOOLEAN RequestInterrupt = FALSE; PKDPC Dpc = Timer->Dpc; ULONG Period = Timer->Period; LARGE_INTEGER Interval, SystemTime; DPRINT("KiSignalTimer(): Timer %p\n", Timer);
/* Set default values */ Timer->Header.Inserted = FALSE; Timer->Header.SignalState = TRUE;
/* Check if the timer has waiters */ if (!IsListEmpty(&Timer->Header.WaitListHead)) { /* Check the type of event */ if (Timer->Header.Type == TimerNotificationObject) { /* Unwait the thread */ KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT); } else { /* Otherwise unwait the thread and signal the timer */ KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT); } }
/* Check if we have a period */ if (Period) { /* Calculate the interval and insert the timer */ Interval.QuadPart = Int32x32To64(Period, -10000); while (!KiInsertTreeTimer(Timer, Interval)); }
/* Check if we have a DPC */ if (Dpc) { /* Insert it in the queue */ KeQuerySystemTime(&SystemTime); KeInsertQueueDpc(Dpc, ULongToPtr(SystemTime.LowPart), ULongToPtr(SystemTime.HighPart)); RequestInterrupt = TRUE; }
/* Return whether we need to request a DPC interrupt or not */ return RequestInterrupt;}
VOIDFASTCALLKiCompleteTimer(IN PKTIMER Timer, IN PKSPIN_LOCK_QUEUE LockQueue){ LIST_ENTRY ListHead; BOOLEAN RequestInterrupt = FALSE; DPRINT("KiCompleteTimer(): Timer %p, LockQueue: %p\n", Timer, LockQueue);
/* Remove it from the timer list */ KiRemoveEntryTimer(Timer);
/* Link the timer list to our stack */ ListHead.Flink = &Timer->TimerListEntry; ListHead.Blink = &Timer->TimerListEntry; Timer->TimerListEntry.Flink = &ListHead; Timer->TimerListEntry.Blink = &ListHead;
/* Release the timer lock */ KiReleaseTimerLock(LockQueue);
/* Acquire dispatcher lock */ KiAcquireDispatcherLockAtSynchLevel();
/* Signal the timer if it's still on our list */ if (!IsListEmpty(&ListHead)) RequestInterrupt = KiSignalTimer(Timer);
/* Release the dispatcher lock */ KiReleaseDispatcherLockFromSynchLevel();
/* Request a DPC if needed */ if (RequestInterrupt) HalRequestSoftwareInterrupt(DISPATCH_LEVEL);}
/* PUBLIC FUNCTIONS **********************************************************/
/* * @implemented */BOOLEANNTAPIKeCancelTimer(IN OUT PKTIMER Timer){ KIRQL OldIrql; BOOLEAN Inserted; ASSERT_TIMER(Timer); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); DPRINT("KeCancelTimer(): Timer %p\n", Timer);
/* Lock the Database and Raise IRQL */ OldIrql = KiAcquireDispatcherLock();
/* Check if it's inserted, and remove it if it is */ Inserted = Timer->Header.Inserted; if (Inserted) KxRemoveTreeTimer(Timer);
/* Release Dispatcher Lock */ KiReleaseDispatcherLock(OldIrql);
/* Return the old state */ return Inserted;}
/* * @implemented */VOIDNTAPIKeInitializeTimer(OUT PKTIMER Timer){ /* Call the New Function */ KeInitializeTimerEx(Timer, NotificationTimer);}
/* * @implemented */VOIDNTAPIKeInitializeTimerEx(OUT PKTIMER Timer, IN TIMER_TYPE Type){ DPRINT("KeInitializeTimerEx(): Timer %p, Type %s\n", Timer, (Type == NotificationTimer) ? "NotificationTimer" : "SynchronizationTimer");
/* Initialize the Dispatch Header */ ASSERT((Type == NotificationTimer) || (Type == SynchronizationTimer)); Timer->Header.Type = TimerNotificationObject + Type; //Timer->Header.TimerControlFlags = 0; // win does not init this field Timer->Header.Hand = sizeof(KTIMER) / sizeof(ULONG); Timer->Header.Inserted = 0; // win7: Timer->Header.TimerMiscFlags = 0; Timer->Header.SignalState = 0; InitializeListHead(&(Timer->Header.WaitListHead));
/* Initialize the Other data */ Timer->DueTime.QuadPart = 0; Timer->Period = 0;}
/* * @implemented */BOOLEANNTAPIKeReadStateTimer(IN PKTIMER Timer){ /* Return the Signal State */ ASSERT_TIMER(Timer); return (BOOLEAN)Timer->Header.SignalState;}
/* * @implemented */BOOLEANNTAPIKeSetTimer(IN OUT PKTIMER Timer, IN LARGE_INTEGER DueTime, IN PKDPC Dpc OPTIONAL){ /* Call the newer function and supply a period of 0 */ return KeSetTimerEx(Timer, DueTime, 0, Dpc);}
/* * @implemented */BOOLEANNTAPIKeSetTimerEx(IN OUT PKTIMER Timer, IN LARGE_INTEGER DueTime, IN LONG Period, IN PKDPC Dpc OPTIONAL){ KIRQL OldIrql; BOOLEAN Inserted; ULONG Hand = 0; BOOLEAN RequestInterrupt = FALSE; ASSERT_TIMER(Timer); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); DPRINT("KeSetTimerEx(): Timer %p, DueTime %I64d, Period %d, Dpc %p\n", Timer, DueTime.QuadPart, Period, Dpc);
/* Lock the Database and Raise IRQL */ OldIrql = KiAcquireDispatcherLock();
/* Check if it's inserted, and remove it if it is */ Inserted = Timer->Header.Inserted; if (Inserted) KxRemoveTreeTimer(Timer);
/* Set Default Timer Data */ Timer->Dpc = Dpc; Timer->Period = Period; if (!KiComputeDueTime(Timer, DueTime, &Hand)) { /* Signal the timer */ RequestInterrupt = KiSignalTimer(Timer);
/* Release the dispatcher lock */ KiReleaseDispatcherLockFromSynchLevel();
/* Check if we need to do an interrupt */ if (RequestInterrupt) HalRequestSoftwareInterrupt(DISPATCH_LEVEL); } else { /* Insert the timer */ Timer->Header.SignalState = FALSE; KxInsertTimer(Timer, Hand); }
/* Exit the dispatcher */ KiExitDispatcher(OldIrql);
/* Return old state */ return Inserted;}