- Rewrite kernel timer implementation to use Windows 2003's hash-based table timer model. Extremely scalable, since each timer list has its own lock, and optimized for speed since timers are hashed with the tick count at each update (timer.c).

- Implement a new timer expiration function which takes advantage of the fact that timers are now hashed with the system tickcount, and thus only parses the "hot" lists (dpc.c).
- Detect timer expiration during KeUpdateSystemTime and edit the KPRCB to notify of timer expiration. This will be picked up by the timer expiration DPC (clock.s).
- Add support for new timer code in dispatcher (wait.c, queue.c, ke_x.h).
- Change system startup to support the new timer DPC/list (krnlinit.c, clock.c).
- Remove corresponding entry from kernel fun, and update the guidance plan with recent successes and setbacks. This patch is likely the last big architectural change in the kernel except for enabling the new scheduler at a later time.

svn path=/trunk/; revision=25611
This commit is contained in:
Alex Ionescu 2007-01-24 06:50:28 +00:00
parent 991fe7200a
commit f1e71db26d
10 changed files with 1192 additions and 748 deletions

View file

@ -8,9 +8,6 @@
// Do NOT ask when it will be fixed.
// Failure to respect this will *ACHIEVE NOTHING*.
//
// Ke2:
// - Dispatcher Rewrite (DPCs-Timers-Waits).
//
// Hal:
// - Use APC and DPC Interrupt Dispatchers.
// - CMOS Initialization and CMOS Spinlock.
@ -26,14 +23,15 @@
// REACTOS GUIDANCE PLAN
// ________________________________________________________________________________________________________
// / \
// | OB, PS, LPC, DBGK, IO => Almost entirely fixed interaction with Ke/Ex. | |
// | OB, PS, LPC, DBGK, EX => "Code complete". No expected changes until 0.5.0 | |
// | SE => Not looked at. Interaction with Ps/Io is minimal and currently hacked away. Preserve. |J|
// | EX => Needs re-visiting (in trunk). Do callbacks/push locks for interaction with Ps. |A|
// | INIT => Boot sequence still needs work in terms of interaction with Ke and CPU features. |A|
// | KD/KDBG => Laptop has special version of ROS without these components. Commit in branch. |N|
// | INIT => Boot sequence still needs work in terms of interaction with Ke and CPU features. | |
// | || || || || || || || || || || || || |F|
// | \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ |E|
// | HAL => Needs APC/DPC/IRQL implementation fixed ASAP in terms of interaction with Ke. |B|
// | HAL => Needs APC/DPC/IRQL implementation fixed ASAP in terms of interaction with Ke. | |
// | || || || || || || || || || || || || | |
// | \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ |F|
// | KE => Enable new thread scheduler and ensure it works. |E|
// | KD => Implement KD64 6.0, compatible with WinDBG |B|
// | FSTUB => Needs IoAssignDriveLetters fixed ASAP but not critical to Ke/Ex. Interacts with Io. | |
// | || || || || || || || || || || || || |M|
// | \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ |A|
@ -41,7 +39,7 @@
// | || || || || || || || || || || || || | |
// | || || || || || || || || || || || || |A|
// | \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ |P|
// | KE => Timer Rewrite + Thread Scheduler Rewrite. |R|
// | PNPMGR => TBD |R|
// | || || || || || || || || || || || || |I|
// | || || || || || || || || || || || || |L|
// | || || || || || || || || || || || || | |

View file

@ -55,6 +55,15 @@ typedef struct _KTIMER_TABLE_ENTRY
ULARGE_INTEGER Time;
} KTIMER_TABLE_ENTRY, *PKTIMER_TABLE_ENTRY;
#define MAX_TIMER_DPCS 16
typedef struct _DPC_QUEUE_ENTRY
{
PKDPC Dpc;
PKDEFERRED_ROUTINE Routine;
PVOID Context;
} DPC_QUEUE_ENTRY, *PDPC_QUEUE_ENTRY;
typedef PCHAR
(NTAPI *PKE_BUGCHECK_UNICODE_TO_ANSI)(
IN PUNICODE_STRING Unicode,
@ -122,9 +131,8 @@ extern ULONG KiTimeLimitIsrMicroseconds;
extern ULONG KiServiceLimit;
extern LIST_ENTRY BugcheckCallbackListHead, BugcheckReasonCallbackListHead;
extern KSPIN_LOCK BugCheckCallbackLock;
extern KDPC KiExpireTimerDpc;
extern KDPC KiTimerExpireDpc;
extern KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE];
extern LIST_ENTRY KiTimerListHead;
extern FAST_MUTEX KiGenericCallDpcMutex;
extern LIST_ENTRY KiProfileListHead, KiProfileSourceListHead;
extern KSPIN_LOCK KiProfileLock;
@ -258,6 +266,27 @@ CPUID(
IN ULONG InfoType
);
BOOLEAN
FASTCALL
KiInsertTimerTable(
IN PKTIMER Timer,
IN ULONG Hand
);
BOOLEAN
FASTCALL
KiInsertTreeTimer(
IN PKTIMER Timer,
IN LARGE_INTEGER Interval
);
VOID
FASTCALL
KiCompleteTimer(
IN PKTIMER Timer,
IN PKSPIN_LOCK_QUEUE LockQueue
);
/* gmutex.c ********************************************************************/
VOID
@ -562,13 +591,6 @@ BOOLEAN
NTAPI
KeDisableThreadApcQueueing(IN PKTHREAD Thread);
BOOLEAN
NTAPI
KiInsertTimer(
PKTIMER Timer,
LARGE_INTEGER DueTime
);
VOID
FASTCALL
KiWaitTest(
@ -598,6 +620,21 @@ KiInsertQueue(
BOOLEAN Head
);
VOID
NTAPI
KiTimerExpiration(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
ULONG
NTAPI
KiComputeTimerTableIndex(
IN LONGLONG TimeValue
);
ULONG
NTAPI
KeSetProcess(

File diff suppressed because it is too large Load diff

View file

@ -27,7 +27,7 @@
/* GLOBALS ****************************************************************/
LARGE_INTEGER KeBootTime, KeBootTimeBias;
KDPC KiExpireTimerDpc;
KDPC KiTimerExpireDpc;
BOOLEAN KiClockSetupComplete = FALSE;
ULONG KiTimeLimitIsrMicroseconds;
@ -37,7 +37,6 @@ ULONG KiTimeLimitIsrMicroseconds;
volatile KSYSTEM_TIME KeTickCount = {0};
volatile ULONG KiRawTicks = 0;
LONG KiTickOffset = 0;
extern LIST_ENTRY KiTimerListHead;
/*
* The increment in the system clock every timer tick (in system time units)

View file

@ -2,7 +2,7 @@
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ke/dpc.c
* PURPOSE: Routines for CPU-level support
* PURPOSE: Deferred Procedure Call (DPC) Support
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Philip Susi (phreak@iag.net)
* Eric Kohl (ekohl@abo.rhein-zeitung.de)
@ -25,14 +25,231 @@ FAST_MUTEX KiGenericCallDpcMutex;
/* PRIVATE FUNCTIONS *********************************************************/
//
// This routine executes at the end of a thread's quantum.
// If the thread's quantum has expired, then a new thread is attempted
// to be scheduled.
//
// If no candidate thread has been found, the routine will return, otherwise
// it will swap contexts to the next scheduled thread.
//
VOID
NTAPI
KiTimerExpiration(IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2)
{
LARGE_INTEGER SystemTime, InterruptTime, Interval;
LONG Limit, Index, i;
ULONG Timers, ActiveTimers, DpcCalls;
PLIST_ENTRY ListHead, NextEntry;
PKTIMER_TABLE_ENTRY TimerEntry;
KIRQL OldIrql;
PKTIMER Timer;
PKDPC TimerDpc;
ULONG Period;
DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS];
PKSPIN_LOCK_QUEUE LockQueue;
/* Disable interrupts */
_disable();
/* Query system and interrupt time */
KeQuerySystemTime(&SystemTime);
InterruptTime.QuadPart = KeQueryInterruptTime();
Limit = KeTickCount.LowPart;
/* Bring interrupts back */
_enable();
/* Get the index of the timer and normalize it */
Index = PtrToLong(SystemArgument1);
if ((Limit - Index) >= TIMER_TABLE_SIZE)
{
/* Normalize it */
Limit = Index + TIMER_TABLE_SIZE - 1;
}
/* Setup index and actual limit */
Index--;
Limit &= (TIMER_TABLE_SIZE - 1);
/* Setup accounting data */
DpcCalls = 0;
Timers = 24;
ActiveTimers = 4;
/* Lock the Database and Raise IRQL */
OldIrql = KiAcquireDispatcherLock();
/* Start expiration loop */
do
{
/* Get the current index */
Index = (Index + 1) & (TIMER_TABLE_SIZE - 1);
/* Get list pointers and loop the list */
ListHead = &KiTimerTableListHead[Index].Entry;
while (ListHead != ListHead->Flink)
{
/* Lock the timer and go to the next entry */
LockQueue = KiAcquireTimerLock(Index);
NextEntry = ListHead->Flink;
/* Get the current timer and check its due time */
Timers--;
Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
if ((NextEntry != ListHead) &&
(Timer->DueTime.QuadPart <= InterruptTime.QuadPart))
{
/* It's expired, remove it */
ActiveTimers--;
if (RemoveEntryList(&Timer->TimerListEntry))
{
/* Get the entry and check if it's empty */
TimerEntry = &KiTimerTableListHead[Timer->Header.Hand];
if (IsListEmpty(&TimerEntry->Entry))
{
/* Clear the time then */
TimerEntry->Time.HighPart = 0xFFFFFFFF;
}
}
/* Make it non-inserted, unlock it, and signal it */
Timer->Header.Inserted = FALSE;
KiReleaseTimerLock(LockQueue);
Timer->Header.SignalState = 1;
/* Get the DPC and period */
TimerDpc = Timer->Dpc;
Period = Timer->Period;
/* Check if there's any 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 (TimerDpc)
{
/* Setup the DPC Entry */
DpcEntry[DpcCalls].Dpc = TimerDpc;
DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine;
DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext;
DpcCalls++;
}
/* Check if we're done processing */
if (!(ActiveTimers) || !(Timers))
{
/* Release the dispatcher while doing DPCs */
KiReleaseDispatcherLock(DISPATCH_LEVEL);
/* Start looping all DPC Entries */
for (i = 0; DpcCalls; DpcCalls--, i++)
{
/* Call the DPC */
DpcEntry[i].Routine(DpcEntry[i].Dpc,
DpcEntry[i].Context,
UlongToPtr(SystemTime.LowPart),
UlongToPtr(SystemTime.HighPart));
}
/* Reset accounting */
Timers = 24;
ActiveTimers = 4;
/* Lock the dispatcher database */
KiAcquireDispatcherLock();
}
}
else
{
/* Check if the timer list is empty */
if (NextEntry != ListHead)
{
/* Sanity check */
ASSERT(KiTimerTableListHead[Index].Time.QuadPart <=
Timer->DueTime.QuadPart);
/* Update the time */
_disable();
KiTimerTableListHead[Index].Time.QuadPart =
Timer->DueTime.QuadPart;
_enable();
}
/* Release the lock */
KiReleaseTimerLock(LockQueue);
/* Check if we've scanned all the timers we could */
if (!Timers)
{
/* Release the dispatcher while doing DPCs */
KiReleaseDispatcherLock(DISPATCH_LEVEL);
/* Start looping all DPC Entries */
for (i = 0; DpcCalls; DpcCalls--, i++)
{
/* Call the DPC */
DpcEntry[i].Routine(DpcEntry[i].Dpc,
DpcEntry[i].Context,
UlongToPtr(SystemTime.LowPart),
UlongToPtr(SystemTime.HighPart));
}
/* Reset accounting */
Timers = 24;
ActiveTimers = 4;
/* Lock the dispatcher database */
KiAcquireDispatcherLock();
}
/* Done looping */
break;
}
}
} while (Index != Limit);
/* Check if we still have DPC entries */
if (DpcCalls)
{
/* Release the dispatcher while doing DPCs */
KiReleaseDispatcherLock(DISPATCH_LEVEL);
/* Start looping all DPC Entries */
for (i = 0; DpcCalls; DpcCalls--, i++)
{
/* Call the DPC */
DpcEntry[i].Routine(DpcEntry[i].Dpc,
DpcEntry[i].Context,
UlongToPtr(SystemTime.LowPart),
UlongToPtr(SystemTime.HighPart));
}
/* Lower IRQL if we need to */
if (OldIrql != DISPATCH_LEVEL) KeLowerIrql(OldIrql);
}
else
{
/* Unlock the dispatcher */
KiReleaseDispatcherLock(OldIrql);
}
}
VOID
NTAPI
KiQuantumEnd(VOID)
@ -146,6 +363,7 @@ KiRetireDpcList(IN PKPRCB Prcb)
PKDPC Dpc;
PKDEFERRED_ROUTINE DeferredRoutine;
PVOID DeferredContext, SystemArgument1, SystemArgument2;
ULONG_PTR TimerHand;
/* Main outer loop */
do
@ -156,8 +374,11 @@ KiRetireDpcList(IN PKPRCB Prcb)
/* Check if this is a timer expiration request */
if (Prcb->TimerRequest)
{
/* FIXME: Not yet implemented */
ASSERT(FALSE);
TimerHand = Prcb->TimerHand;
Prcb->TimerRequest = 0;
_enable();
KiTimerExpiration(NULL, NULL, (PVOID) TimerHand, NULL);
_disable();
}
/* Loop while we have entries in the queue */

View file

@ -13,6 +13,45 @@
/* FUNCTIONS ******************************************************************/
.globl _KiComputeTimerTableIndex@8
.func KiComputeTimerTableIndex@8
_KiComputeTimerTableIndex@8:
/* Save registers */
push ebx
/* Make the first multiplication */
mov eax, [esp+8]
mul dword ptr [_KiTimeIncrementReciprocal+4]
mov ebx, eax
mov ecx, edx
/* Make the second multiplication */
mov eax, [esp+12]
mul dword ptr [_KiTimeIncrementReciprocal]
add ebx, eax
adc ecx, edx
/* Multiply by the reciprocal */
mov eax, [esp+8]
mul dword ptr [_KiTimeIncrementReciprocal]
mov eax, [esp+12]
push edx
mul dword ptr [_KiTimeIncrementReciprocal+4]
pop edx
add edx, ebx
adc eax, ecx
/* Shift the result and generate the index */
mov cl, [_KiTimeIncrementShiftCount]
shr eax, cl
and eax, TIMER_TABLE_SIZE - 1
/* Return */
pop ebx
ret 8
.endfunc
.globl _KeUpdateRunTime@4
.func KeUpdateRunTime@4
_KeUpdateRunTime@4:
@ -214,13 +253,50 @@ _KeUpdateSystemTime@0:
/* FIXME: HACK */
mov ds:[USER_SHARED_DATA], ecx
/* Get hand index and entry into the table */
and eax, TIMER_TABLE_SIZE - 1
shl eax, 4
/* Compare the due time */
cmp esi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME+4]
jb NextHand
ja TimerExpired
cmp edi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME]
jnb TimerExpired
NextHand:
/* Move to the next hand */
inc ebx
mov eax, ebx
IncompleteTick:
/* FIXME: NASTY Queue DPC to handle registered timers */
push 0
push [esp+KTRAP_FRAME_EIP]
push offset _KiExpireTimerDpc
call _KeInsertQueueDpc@12
/* Get hand index and entry into the table */
and eax, TIMER_TABLE_SIZE - 1
shl eax, 4
/* Compare the due time */
cmp esi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME+4]
jb DebugCheck
ja TimerExpired
cmp edi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME]
jb DebugCheck
TimerExpired:
/* Check if expiration is active */
mov ecx, [fs:KPCR_PRCB]
cmp dword ptr [ecx+KPRCB_TIMER_REQUEST], 0
jne DebugCheck
/* It's not, register it */
mov [ecx+KPRCB_TIMER_REQUEST], esp
mov [ecx+KPRCB_TIMER_HAND], ebx
mov ecx, DISPATCH_LEVEL
call @HalRequestSoftwareInterrupt@4
DebugCheck:
/* FIXME: Check for KdDebuggerEnabled */
/* Check if this was a full tick */
cmp dword ptr _KiTickOffset, 0

View file

@ -67,8 +67,8 @@ KiInitSystem(VOID)
KeInitializeSpinLock(&BugCheckCallbackLock);
/* Initialize the Timer Expiration DPC */
KeInitializeDpc(&KiExpireTimerDpc, KiExpireTimers, NULL);
KeSetTargetProcessorDpc(&KiExpireTimerDpc, 0);
KeInitializeDpc(&KiTimerExpireDpc, KiTimerExpiration, NULL);
KeSetTargetProcessorDpc(&KiTimerExpireDpc, 0);
/* Initialize Profiling data */
KeInitializeSpinLock(&KiProfileLock);
@ -84,9 +84,6 @@ KiInitSystem(VOID)
KiTimerTableListHead[i].Time.LowPart = 0;
}
/* Initialize old-style list */
InitializeListHead(&KiTimerListHead);
/* Initialize the Swap event and all swap lists */
KeInitializeEvent(&KiSwapEvent, SynchronizationEvent, FALSE);
InitializeListHead(&KiProcessInSwapListHead);
@ -293,3 +290,5 @@ KeInitSystem(VOID)
return TRUE;
}

View file

@ -75,6 +75,7 @@ KiInsertQueue(IN PKQUEUE Queue,
PKTHREAD Thread = KeGetCurrentThread();
PKWAIT_BLOCK WaitBlock;
PLIST_ENTRY WaitEntry;
PKTIMER Timer;
ASSERT_QUEUE(Queue);
/* Save the old state */
@ -109,12 +110,8 @@ KiInsertQueue(IN PKQUEUE Queue,
Thread->WaitReason = 0;
/* Check if there's a Thread Timer */
if (Thread->Timer.Header.Inserted)
{
/* Cancel the Thread Timer with the no-lock fastpath */
Thread->Timer.Header.Inserted = FALSE;
RemoveEntryList(&Thread->Timer.TimerListEntry);
}
Timer = &Thread->Timer;
if (Timer->Header.Inserted) KxRemoveTreeTimer(Timer);
/* Reschedule the Thread */
KiReadyThread(Thread);
@ -251,6 +248,7 @@ KeRemoveQueue(IN PKQUEUE Queue,
BOOLEAN Swappable;
PLARGE_INTEGER OriginalDueTime = Timeout;
LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
ULONG Hand = 0;
ASSERT_QUEUE(Queue);
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
@ -390,7 +388,7 @@ KeRemoveQueue(IN PKQUEUE Queue,
if (Timeout)
{
/* Insert it */
KiInsertWaitTimer(Timer);
KxInsertTimer(Timer, Hand);
}
else
{

View file

@ -4,188 +4,242 @@
* FILE: ntoskrnl/ke/timer.c
* PURPOSE: Handle Kernel Timers (Kernel-part of Executive Timers)
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* David Welch (welch@mcmail.com)
*/
/* INCLUDES ***************************************************************/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <internal/debug.h>
#include <debug.h>
/* GLOBALS ****************************************************************/
/* GLOBALS *******************************************************************/
KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE];
LARGE_INTEGER KiTimeIncrementReciprocal;
UCHAR KiTimeIncrementShiftCount;
LIST_ENTRY KiTimerListHead;
KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE];
#define SYSTEM_TIME_UNITS_PER_MSEC (10000)
/* PRIVATE FUNCTIONS ******************************************************/
/* PRIVATE FUNCTIONS *********************************************************/
VOID
NTAPI
KiRemoveTimer(IN PKTIMER Timer)
{
/* Remove the timer */
Timer->Header.Inserted = FALSE;
RemoveEntryList(&Timer->TimerListEntry);
}
/*
* Note: This function is called with the Dispatcher Lock held.
*/
BOOLEAN
NTAPI
KiInsertTimer(IN PKTIMER Timer,
IN LARGE_INTEGER DueTime)
FASTCALL
KiInsertTreeTimer(IN PKTIMER Timer,
IN LARGE_INTEGER Interval)
{
LARGE_INTEGER SystemTime;
LARGE_INTEGER DifferenceTime;
ULONGLONG InterruptTime;
/* Set default data */
Timer->Header.Inserted = TRUE;
Timer->Header.Absolute = FALSE;
if (!Timer->Period) Timer->Header.SignalState = FALSE;
BOOLEAN Inserted = FALSE;
ULONG Hand = 0;
PKSPIN_LOCK_QUEUE LockQueue;
LONGLONG DueTime;
LARGE_INTEGER InterruptTime, SystemTime, DifferenceTime;
PKTIMER_TABLE_ENTRY TimerEntry;
/* Convert to relative time if needed */
if (DueTime.HighPart >= 0)
Timer->Header.Absolute = FALSE;
if (Interval.HighPart >= 0)
{
/* Get System Time */
KeQuerySystemTime(&SystemTime);
/* Do the conversion */
DifferenceTime.QuadPart = SystemTime.QuadPart - DueTime.QuadPart;
DifferenceTime.QuadPart = SystemTime.QuadPart - Interval.QuadPart;
/* Make sure it hasn't already expired */
Timer->Header.Absolute = TRUE;
if (DifferenceTime.HighPart >= 0)
{
/* Cancel everything */
Timer->Header.SignalState = TRUE;
Timer->Header.Inserted = FALSE;
Timer->Header.Hand = 0;
Timer->DueTime.QuadPart = 0;
return FALSE;
}
/* Set the time as Absolute */
Timer->Header.Absolute = TRUE;
DueTime = DifferenceTime;
Interval = DifferenceTime;
}
/* Get the Interrupt Time */
InterruptTime = KeQueryInterruptTime();
InterruptTime.QuadPart = KeQueryInterruptTime();
/* Set the Final Due Time */
Timer->DueTime.QuadPart = InterruptTime - DueTime.QuadPart;
/* Recalculate due time */
DueTime = InterruptTime.QuadPart - Interval.QuadPart;
Timer->DueTime.QuadPart = DueTime;
/* Now insert it into the Timer List */
InsertAscendingList(&KiTimerListHead,
Timer,
KTIMER,
TimerListEntry,
DueTime.QuadPart);
return TRUE;
/* Get the handle */
Hand = KiComputeTimerTableIndex(DueTime);
Timer->Header.Hand = (UCHAR)Hand;
Timer->Header.Inserted = TRUE;
/* Acquire the lock */
LockQueue = KiAcquireTimerLock(Hand);
/* Insert the timer */
if (KiInsertTimerTable(Timer, Hand))
{
/* It was already there, remove it */
if (RemoveEntryList(&Timer->TimerListEntry))
{
/* Get the entry and check if it's empty */
TimerEntry = &KiTimerTableListHead[Hand];
if (IsListEmpty(&TimerEntry->Entry))
{
/* Clear the time then */
TimerEntry->Time.HighPart = 0xFFFFFFFF;
}
}
}
else
{
/* Otherwise, we're now inserted */
Inserted = TRUE;
}
/* Release the lock and return insert status */
return Inserted;
}
/*
* We enter this function at IRQL DISPATCH_LEVEL, and with the
* Dispatcher Lock held!
*/
VOID
NTAPI
KiHandleExpiredTimer(IN PKTIMER Timer)
BOOLEAN
FASTCALL
KiInsertTimerTable(IN PKTIMER Timer,
IN ULONG Hand)
{
LARGE_INTEGER DueTime;
/* Set it as Signaled */
Timer->Header.SignalState = TRUE;
/* Check if it has any waiters */
if (!IsListEmpty(&Timer->Header.WaitListHead))
{
/* Wake them */
KiWaitTest(Timer, IO_NO_INCREMENT);
}
/* If the Timer is periodic, reinsert the timer with the new due time */
if (Timer->Period)
{
/* Reinsert the Timer */
DueTime.QuadPart = Timer->Period * -SYSTEM_TIME_UNITS_PER_MSEC;
while (!KiInsertTimer(Timer, DueTime));
}
/* Check if the Timer has a DPC */
if (Timer->Dpc)
{
/* Insert the DPC */
KeInsertQueueDpc(Timer->Dpc,
NULL,
NULL);
}
}
VOID
NTAPI
KiExpireTimers(IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2)
{
PKTIMER Timer;
ULONGLONG InterruptTime;
LIST_ENTRY ExpiredTimerList;
LARGE_INTEGER InterruptTime;
LONGLONG DueTime = Timer->DueTime.QuadPart;
BOOLEAN Expired = FALSE;
PLIST_ENTRY ListHead, NextEntry;
KIRQL OldIrql;
PKTIMER CurrentTimer;
/* Initialize the Expired Timer List */
InitializeListHead(&ExpiredTimerList);
/* Check if the period is zero */
if (!Timer->Period) Timer->Header.SignalState = FALSE;
/* Lock the Database and Raise IRQL */
OldIrql = KiAcquireDispatcherLock();
/* Sanity check */
ASSERT(Hand == KiComputeTimerTableIndex(DueTime));
/* Query Interrupt Times */
InterruptTime = KeQueryInterruptTime();
/* Loop through the Timer List */
ListHead = &KiTimerListHead;
NextEntry = ListHead->Flink;
/* Loop the timer list backwards */
ListHead = &KiTimerTableListHead[Hand].Entry;
NextEntry = ListHead->Blink;
while (NextEntry != ListHead)
{
/* Get the timer */
Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
CurrentTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
/* Check if we have to Expire it */
if (InterruptTime < Timer->DueTime.QuadPart) break;
/* Now check if we can fit it before */
if ((ULONGLONG)DueTime >= CurrentTimer->DueTime.QuadPart) break;
/* Remove it from the Timer List, add it to the Expired List */
RemoveEntryList(&Timer->TimerListEntry);
InsertTailList(&ExpiredTimerList, &Timer->TimerListEntry);
NextEntry = ListHead->Flink;
/* Keep looping */
NextEntry = NextEntry->Blink;
}
/* Expire the Timers */
while (ExpiredTimerList.Flink != &ExpiredTimerList)
/* 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)
{
/* Get the Timer */
Timer = CONTAINING_RECORD(ExpiredTimerList.Flink,
KTIMER,
TimerListEntry);
/* Set the time */
KiTimerTableListHead[Hand].Time.QuadPart = DueTime;
/* Remove it */
///
// GCC IS A BRAINDEAD PIECE OF SHIT. WILL GIVE 5$ FOR EACH DEV KILLED.
///
Timer->Header.Inserted = FALSE;
RemoveEntryList(&Timer->TimerListEntry);
//KiRemoveTimer(Timer);
/* Expire it */
KiHandleExpiredTimer(Timer);
/* Make sure it hasn't expired already */
InterruptTime.QuadPart = KeQueryInterruptTime();
if (DueTime <= InterruptTime.QuadPart) Expired = TRUE;
}
/* Release Dispatcher Lock */
KiReleaseDispatcherLock(OldIrql);
/* Return expired state */
return Expired;
}
BOOLEAN
FASTCALL
KiSignalTimer(IN PKTIMER Timer)
{
BOOLEAN RequestInterrupt = FALSE;
PKDPC Dpc = Timer->Dpc;
ULONG Period = Timer->Period;
LARGE_INTEGER Interval, SystemTime;
/* 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;
}
VOID
FASTCALL
KiCompleteTimer(IN PKTIMER Timer,
IN PKSPIN_LOCK_QUEUE LockQueue)
{
LIST_ENTRY ListHead;
PKTIMER_TABLE_ENTRY TimerEntry;
BOOLEAN RequestInterrupt = FALSE;
/* Remove it from the timer list */
if (RemoveEntryList(&Timer->TimerListEntry))
{
/* Get the entry and check if it's empty */
TimerEntry = &KiTimerTableListHead[Timer->Header.Hand];
if (IsListEmpty(&TimerEntry->Entry))
{
/* Clear the time then */
TimerEntry->Time.HighPart = 0xFFFFFFFF;
}
}
/* 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 */
KiAcquireDispatcherLockAtDpcLevel();
/* Signal the timer if it's still on our list */
if (!IsListEmpty(&ListHead)) RequestInterrupt = KiSignalTimer(Timer);
/* Release the dispatcher lock */
KiReleaseDispatcherLockFromDpcLevel();
/* Request a DPC if needed */
if (RequestInterrupt) HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
}
/* PUBLIC FUNCTIONS **********************************************************/
@ -200,25 +254,19 @@ KeCancelTimer(IN OUT PKTIMER Timer)
KIRQL OldIrql;
BOOLEAN Inserted;
ASSERT_TIMER(Timer);
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
/* 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)
{
///
// GCC IS A BRAINDEAD PIECE OF SHIT. WILL GIVE 5$ FOR EACH DEV KILLED.
///
Timer->Header.Inserted = FALSE;
RemoveEntryList(&Timer->TimerListEntry);
//KiRemoveTimer(Timer);
}
if (Inserted) KxRemoveTreeTimer(Timer);
/* Release Dispatcher Lock */
KiReleaseDispatcherLock(OldIrql);
/* Return the old state */
return Inserted;
}
@ -289,62 +337,75 @@ KeSetTimerEx(IN OUT PKTIMER Timer,
{
KIRQL OldIrql;
BOOLEAN Inserted;
ULONG Hand = 0;
LARGE_INTEGER InterruptTime, SystemTime, DifferenceTime;
BOOLEAN RequestInterrupt = FALSE;
ASSERT_TIMER(Timer);
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
/* 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)
{
///
// GCC IS A BRAINDEAD PIECE OF SHIT. WILL GIVE 5$ FOR EACH DEV KILLED.
///
Timer->Header.Inserted = FALSE;
RemoveEntryList(&Timer->TimerListEntry);
//KiRemoveTimer(Timer);
}
if (Inserted) KxRemoveTreeTimer(Timer);
/* Set Default Timer Data */
Timer->Dpc = Dpc;
Timer->Period = Period;
Timer->Header.SignalState = FALSE;
/* Insert it */
if (!KiInsertTimer(Timer, DueTime))
/* Convert to relative time if needed */
Timer->Header.Absolute = FALSE;
if (DueTime.HighPart >= 0)
{
/* Check if it has any waiters */
if (!IsListEmpty(&Timer->Header.WaitListHead))
/* Get System Time */
KeQuerySystemTime(&SystemTime);
/* Do the conversion */
DifferenceTime.QuadPart = SystemTime.QuadPart - DueTime.QuadPart;
/* Make sure it hasn't already expired */
Timer->Header.Absolute = TRUE;
if (DifferenceTime.HighPart >= 0)
{
/* Wake them */
KiWaitTest(Timer, IO_NO_INCREMENT);
/* Cancel everything */
Timer->Header.SignalState = TRUE;
Timer->Header.Hand = 0;
Timer->DueTime.QuadPart = 0;
/* Signal the timer */
RequestInterrupt = KiSignalTimer(Timer);
/* Release the dispatcher lock */
KiReleaseDispatcherLockFromDpcLevel();
/* Check if we need to do an interrupt */
if (RequestInterrupt) HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
}
/* Check if the Timer has a DPC */
if (Dpc)
{
/* Insert the DPC */
KeInsertQueueDpc(Timer->Dpc,
NULL,
NULL);
}
/* Check if the Timer is periodic */
if (Timer->Period)
{
/* Reinsert the Timer */
DueTime.QuadPart = Timer->Period * -SYSTEM_TIME_UNITS_PER_MSEC;
while (!KiInsertTimer(Timer, DueTime));
}
/* Set the time as Absolute */
DueTime = DifferenceTime;
}
/* Get the Interrupt Time */
InterruptTime.QuadPart = KeQueryInterruptTime();
/* Recalculate due time */
Timer->DueTime.QuadPart = InterruptTime.QuadPart - DueTime.QuadPart;
/* Get the handle */
Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart);
Timer->Header.Hand = (UCHAR)Hand;
Timer->Header.Inserted = TRUE;
/* Insert the timer */
Timer->Header.SignalState = FALSE;
KxInsertTimer(Timer, Hand);
/* Release Dispatcher Lock */
KiReleaseDispatcherLock(OldIrql);
KiExitDispatcher(OldIrql);
/* Return old state */
return Inserted;
}
/* EOF */

View file

@ -77,13 +77,7 @@ KiUnlinkThread(IN PKTHREAD Thread,
/* Check if there's a Thread Timer */
Timer = &Thread->Timer;
if (Timer->Header.Inserted)
{
/* Remove the timer */
Timer->Header.Inserted = FALSE;
RemoveEntryList(&Timer->TimerListEntry);
//KiRemoveTimer(Timer);
}
if (Timer->Header.Inserted) KxRemoveTreeTimer(Timer);
/* Increment the Queue's active threads */
if (Thread->Queue) Thread->Queue->CurrentCount++;
@ -219,6 +213,7 @@ KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode,
BOOLEAN Swappable;
PLARGE_INTEGER OriginalDueTime;
LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
ULONG Hand = 0;
/* If this is a user-mode wait of 0 seconds, yield execution */
if (!(Interval->QuadPart) && (WaitMode != KernelMode))
@ -285,7 +280,7 @@ KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode,
/* Insert the timer and swap the thread */
ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
KiSetThreadSwapBusy(Thread);
KiInsertWaitTimer(Timer);
KxInsertTimer(Timer, Hand);
WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb());
/* Check if were swapped ok */
@ -350,6 +345,7 @@ KeWaitForSingleObject(IN PVOID Object,
BOOLEAN Swappable;
LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
PLARGE_INTEGER OriginalDueTime = Timeout;
ULONG Hand = 0;
/* Check if the lock is already held */
if (!Thread->WaitNext) goto WaitStart;
@ -449,7 +445,7 @@ KeWaitForSingleObject(IN PVOID Object,
if (Timeout)
{
/* Insert it */
KiInsertWaitTimer(Timer);
KxInsertTimer(Timer, Hand);
}
else
{
@ -515,7 +511,7 @@ KeWaitForMultipleObjects(IN ULONG Count,
BOOLEAN Swappable;
PLARGE_INTEGER OriginalDueTime = Timeout;
LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
ULONG Index;
ULONG Index, Hand = 0;
/* Make sure the Wait Count is valid */
if (!WaitBlockArray)
@ -726,7 +722,7 @@ KeWaitForMultipleObjects(IN ULONG Count,
if (Timeout)
{
/* Insert it */
KiInsertWaitTimer(Timer);
KxInsertTimer(Timer, Hand);
}
else
{