mirror of
https://github.com/reactos/reactos.git
synced 2025-05-27 13:08:23 +00:00
- 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:
parent
991fe7200a
commit
f1e71db26d
10 changed files with 1192 additions and 748 deletions
|
@ -8,9 +8,6 @@
|
||||||
// Do NOT ask when it will be fixed.
|
// Do NOT ask when it will be fixed.
|
||||||
// Failure to respect this will *ACHIEVE NOTHING*.
|
// Failure to respect this will *ACHIEVE NOTHING*.
|
||||||
//
|
//
|
||||||
// Ke2:
|
|
||||||
// - Dispatcher Rewrite (DPCs-Timers-Waits).
|
|
||||||
//
|
|
||||||
// Hal:
|
// Hal:
|
||||||
// - Use APC and DPC Interrupt Dispatchers.
|
// - Use APC and DPC Interrupt Dispatchers.
|
||||||
// - CMOS Initialization and CMOS Spinlock.
|
// - CMOS Initialization and CMOS Spinlock.
|
||||||
|
@ -26,14 +23,15 @@
|
||||||
// REACTOS GUIDANCE PLAN
|
// 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|
|
// | 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|
|
// | 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. | |
|
// | HAL => Needs APC/DPC/IRQL implementation fixed ASAP in terms of interaction with Ke. | |
|
||||||
// | || || || || || || || || || || || || |F|
|
// | || || || || || || || || || || || || | |
|
||||||
// | \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ |E|
|
// | \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ |F|
|
||||||
// | HAL => Needs APC/DPC/IRQL implementation fixed ASAP in terms of interaction with Ke. |B|
|
// | 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. | |
|
// | FSTUB => Needs IoAssignDriveLetters fixed ASAP but not critical to Ke/Ex. Interacts with Io. | |
|
||||||
// | || || || || || || || || || || || || |M|
|
// | || || || || || || || || || || || || |M|
|
||||||
// | \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ |A|
|
// | \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ |A|
|
||||||
|
@ -41,7 +39,7 @@
|
||||||
// | || || || || || || || || || || || || | |
|
// | || || || || || || || || || || || || | |
|
||||||
// | || || || || || || || || || || || || |A|
|
// | || || || || || || || || || || || || |A|
|
||||||
// | \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ |P|
|
// | \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ |P|
|
||||||
// | KE => Timer Rewrite + Thread Scheduler Rewrite. |R|
|
// | PNPMGR => TBD |R|
|
||||||
// | || || || || || || || || || || || || |I|
|
// | || || || || || || || || || || || || |I|
|
||||||
// | || || || || || || || || || || || || |L|
|
// | || || || || || || || || || || || || |L|
|
||||||
// | || || || || || || || || || || || || | |
|
// | || || || || || || || || || || || || | |
|
||||||
|
|
|
@ -55,6 +55,15 @@ typedef struct _KTIMER_TABLE_ENTRY
|
||||||
ULARGE_INTEGER Time;
|
ULARGE_INTEGER Time;
|
||||||
} KTIMER_TABLE_ENTRY, *PKTIMER_TABLE_ENTRY;
|
} 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
|
typedef PCHAR
|
||||||
(NTAPI *PKE_BUGCHECK_UNICODE_TO_ANSI)(
|
(NTAPI *PKE_BUGCHECK_UNICODE_TO_ANSI)(
|
||||||
IN PUNICODE_STRING Unicode,
|
IN PUNICODE_STRING Unicode,
|
||||||
|
@ -122,9 +131,8 @@ extern ULONG KiTimeLimitIsrMicroseconds;
|
||||||
extern ULONG KiServiceLimit;
|
extern ULONG KiServiceLimit;
|
||||||
extern LIST_ENTRY BugcheckCallbackListHead, BugcheckReasonCallbackListHead;
|
extern LIST_ENTRY BugcheckCallbackListHead, BugcheckReasonCallbackListHead;
|
||||||
extern KSPIN_LOCK BugCheckCallbackLock;
|
extern KSPIN_LOCK BugCheckCallbackLock;
|
||||||
extern KDPC KiExpireTimerDpc;
|
extern KDPC KiTimerExpireDpc;
|
||||||
extern KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE];
|
extern KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE];
|
||||||
extern LIST_ENTRY KiTimerListHead;
|
|
||||||
extern FAST_MUTEX KiGenericCallDpcMutex;
|
extern FAST_MUTEX KiGenericCallDpcMutex;
|
||||||
extern LIST_ENTRY KiProfileListHead, KiProfileSourceListHead;
|
extern LIST_ENTRY KiProfileListHead, KiProfileSourceListHead;
|
||||||
extern KSPIN_LOCK KiProfileLock;
|
extern KSPIN_LOCK KiProfileLock;
|
||||||
|
@ -258,6 +266,27 @@ CPUID(
|
||||||
IN ULONG InfoType
|
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 ********************************************************************/
|
/* gmutex.c ********************************************************************/
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
|
@ -562,13 +591,6 @@ BOOLEAN
|
||||||
NTAPI
|
NTAPI
|
||||||
KeDisableThreadApcQueueing(IN PKTHREAD Thread);
|
KeDisableThreadApcQueueing(IN PKTHREAD Thread);
|
||||||
|
|
||||||
BOOLEAN
|
|
||||||
NTAPI
|
|
||||||
KiInsertTimer(
|
|
||||||
PKTIMER Timer,
|
|
||||||
LARGE_INTEGER DueTime
|
|
||||||
);
|
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
FASTCALL
|
FASTCALL
|
||||||
KiWaitTest(
|
KiWaitTest(
|
||||||
|
@ -598,6 +620,21 @@ KiInsertQueue(
|
||||||
BOOLEAN Head
|
BOOLEAN Head
|
||||||
);
|
);
|
||||||
|
|
||||||
|
VOID
|
||||||
|
NTAPI
|
||||||
|
KiTimerExpiration(
|
||||||
|
IN PKDPC Dpc,
|
||||||
|
IN PVOID DeferredContext,
|
||||||
|
IN PVOID SystemArgument1,
|
||||||
|
IN PVOID SystemArgument2
|
||||||
|
);
|
||||||
|
|
||||||
|
ULONG
|
||||||
|
NTAPI
|
||||||
|
KiComputeTimerTableIndex(
|
||||||
|
IN LONGLONG TimeValue
|
||||||
|
);
|
||||||
|
|
||||||
ULONG
|
ULONG
|
||||||
NTAPI
|
NTAPI
|
||||||
KeSetProcess(
|
KeSetProcess(
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -27,7 +27,7 @@
|
||||||
/* GLOBALS ****************************************************************/
|
/* GLOBALS ****************************************************************/
|
||||||
|
|
||||||
LARGE_INTEGER KeBootTime, KeBootTimeBias;
|
LARGE_INTEGER KeBootTime, KeBootTimeBias;
|
||||||
KDPC KiExpireTimerDpc;
|
KDPC KiTimerExpireDpc;
|
||||||
BOOLEAN KiClockSetupComplete = FALSE;
|
BOOLEAN KiClockSetupComplete = FALSE;
|
||||||
ULONG KiTimeLimitIsrMicroseconds;
|
ULONG KiTimeLimitIsrMicroseconds;
|
||||||
|
|
||||||
|
@ -37,7 +37,6 @@ ULONG KiTimeLimitIsrMicroseconds;
|
||||||
volatile KSYSTEM_TIME KeTickCount = {0};
|
volatile KSYSTEM_TIME KeTickCount = {0};
|
||||||
volatile ULONG KiRawTicks = 0;
|
volatile ULONG KiRawTicks = 0;
|
||||||
LONG KiTickOffset = 0;
|
LONG KiTickOffset = 0;
|
||||||
extern LIST_ENTRY KiTimerListHead;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The increment in the system clock every timer tick (in system time units)
|
* The increment in the system clock every timer tick (in system time units)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* PROJECT: ReactOS Kernel
|
* PROJECT: ReactOS Kernel
|
||||||
* LICENSE: GPL - See COPYING in the top level directory
|
* LICENSE: GPL - See COPYING in the top level directory
|
||||||
* FILE: ntoskrnl/ke/dpc.c
|
* FILE: ntoskrnl/ke/dpc.c
|
||||||
* PURPOSE: Routines for CPU-level support
|
* PURPOSE: Deferred Procedure Call (DPC) Support
|
||||||
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
||||||
* Philip Susi (phreak@iag.net)
|
* Philip Susi (phreak@iag.net)
|
||||||
* Eric Kohl (ekohl@abo.rhein-zeitung.de)
|
* Eric Kohl (ekohl@abo.rhein-zeitung.de)
|
||||||
|
@ -25,14 +25,231 @@ FAST_MUTEX KiGenericCallDpcMutex;
|
||||||
|
|
||||||
/* PRIVATE FUNCTIONS *********************************************************/
|
/* PRIVATE FUNCTIONS *********************************************************/
|
||||||
|
|
||||||
//
|
VOID
|
||||||
// This routine executes at the end of a thread's quantum.
|
NTAPI
|
||||||
// If the thread's quantum has expired, then a new thread is attempted
|
KiTimerExpiration(IN PKDPC Dpc,
|
||||||
// to be scheduled.
|
IN PVOID DeferredContext,
|
||||||
//
|
IN PVOID SystemArgument1,
|
||||||
// If no candidate thread has been found, the routine will return, otherwise
|
IN PVOID SystemArgument2)
|
||||||
// it will swap contexts to the next scheduled thread.
|
{
|
||||||
//
|
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
|
VOID
|
||||||
NTAPI
|
NTAPI
|
||||||
KiQuantumEnd(VOID)
|
KiQuantumEnd(VOID)
|
||||||
|
@ -146,6 +363,7 @@ KiRetireDpcList(IN PKPRCB Prcb)
|
||||||
PKDPC Dpc;
|
PKDPC Dpc;
|
||||||
PKDEFERRED_ROUTINE DeferredRoutine;
|
PKDEFERRED_ROUTINE DeferredRoutine;
|
||||||
PVOID DeferredContext, SystemArgument1, SystemArgument2;
|
PVOID DeferredContext, SystemArgument1, SystemArgument2;
|
||||||
|
ULONG_PTR TimerHand;
|
||||||
|
|
||||||
/* Main outer loop */
|
/* Main outer loop */
|
||||||
do
|
do
|
||||||
|
@ -156,8 +374,11 @@ KiRetireDpcList(IN PKPRCB Prcb)
|
||||||
/* Check if this is a timer expiration request */
|
/* Check if this is a timer expiration request */
|
||||||
if (Prcb->TimerRequest)
|
if (Prcb->TimerRequest)
|
||||||
{
|
{
|
||||||
/* FIXME: Not yet implemented */
|
TimerHand = Prcb->TimerHand;
|
||||||
ASSERT(FALSE);
|
Prcb->TimerRequest = 0;
|
||||||
|
_enable();
|
||||||
|
KiTimerExpiration(NULL, NULL, (PVOID) TimerHand, NULL);
|
||||||
|
_disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Loop while we have entries in the queue */
|
/* Loop while we have entries in the queue */
|
||||||
|
|
|
@ -13,6 +13,45 @@
|
||||||
|
|
||||||
/* FUNCTIONS ******************************************************************/
|
/* 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
|
.globl _KeUpdateRunTime@4
|
||||||
.func KeUpdateRunTime@4
|
.func KeUpdateRunTime@4
|
||||||
_KeUpdateRunTime@4:
|
_KeUpdateRunTime@4:
|
||||||
|
@ -214,13 +253,50 @@ _KeUpdateSystemTime@0:
|
||||||
/* FIXME: HACK */
|
/* FIXME: HACK */
|
||||||
mov ds:[USER_SHARED_DATA], ecx
|
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:
|
IncompleteTick:
|
||||||
|
|
||||||
/* FIXME: NASTY Queue DPC to handle registered timers */
|
/* Get hand index and entry into the table */
|
||||||
push 0
|
and eax, TIMER_TABLE_SIZE - 1
|
||||||
push [esp+KTRAP_FRAME_EIP]
|
shl eax, 4
|
||||||
push offset _KiExpireTimerDpc
|
|
||||||
call _KeInsertQueueDpc@12
|
/* 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 */
|
/* Check if this was a full tick */
|
||||||
cmp dword ptr _KiTickOffset, 0
|
cmp dword ptr _KiTickOffset, 0
|
||||||
|
|
|
@ -67,8 +67,8 @@ KiInitSystem(VOID)
|
||||||
KeInitializeSpinLock(&BugCheckCallbackLock);
|
KeInitializeSpinLock(&BugCheckCallbackLock);
|
||||||
|
|
||||||
/* Initialize the Timer Expiration DPC */
|
/* Initialize the Timer Expiration DPC */
|
||||||
KeInitializeDpc(&KiExpireTimerDpc, KiExpireTimers, NULL);
|
KeInitializeDpc(&KiTimerExpireDpc, KiTimerExpiration, NULL);
|
||||||
KeSetTargetProcessorDpc(&KiExpireTimerDpc, 0);
|
KeSetTargetProcessorDpc(&KiTimerExpireDpc, 0);
|
||||||
|
|
||||||
/* Initialize Profiling data */
|
/* Initialize Profiling data */
|
||||||
KeInitializeSpinLock(&KiProfileLock);
|
KeInitializeSpinLock(&KiProfileLock);
|
||||||
|
@ -84,9 +84,6 @@ KiInitSystem(VOID)
|
||||||
KiTimerTableListHead[i].Time.LowPart = 0;
|
KiTimerTableListHead[i].Time.LowPart = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize old-style list */
|
|
||||||
InitializeListHead(&KiTimerListHead);
|
|
||||||
|
|
||||||
/* Initialize the Swap event and all swap lists */
|
/* Initialize the Swap event and all swap lists */
|
||||||
KeInitializeEvent(&KiSwapEvent, SynchronizationEvent, FALSE);
|
KeInitializeEvent(&KiSwapEvent, SynchronizationEvent, FALSE);
|
||||||
InitializeListHead(&KiProcessInSwapListHead);
|
InitializeListHead(&KiProcessInSwapListHead);
|
||||||
|
@ -293,3 +290,5 @@ KeInitSystem(VOID)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,7 @@ KiInsertQueue(IN PKQUEUE Queue,
|
||||||
PKTHREAD Thread = KeGetCurrentThread();
|
PKTHREAD Thread = KeGetCurrentThread();
|
||||||
PKWAIT_BLOCK WaitBlock;
|
PKWAIT_BLOCK WaitBlock;
|
||||||
PLIST_ENTRY WaitEntry;
|
PLIST_ENTRY WaitEntry;
|
||||||
|
PKTIMER Timer;
|
||||||
ASSERT_QUEUE(Queue);
|
ASSERT_QUEUE(Queue);
|
||||||
|
|
||||||
/* Save the old state */
|
/* Save the old state */
|
||||||
|
@ -109,12 +110,8 @@ KiInsertQueue(IN PKQUEUE Queue,
|
||||||
Thread->WaitReason = 0;
|
Thread->WaitReason = 0;
|
||||||
|
|
||||||
/* Check if there's a Thread Timer */
|
/* Check if there's a Thread Timer */
|
||||||
if (Thread->Timer.Header.Inserted)
|
Timer = &Thread->Timer;
|
||||||
{
|
if (Timer->Header.Inserted) KxRemoveTreeTimer(Timer);
|
||||||
/* Cancel the Thread Timer with the no-lock fastpath */
|
|
||||||
Thread->Timer.Header.Inserted = FALSE;
|
|
||||||
RemoveEntryList(&Thread->Timer.TimerListEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reschedule the Thread */
|
/* Reschedule the Thread */
|
||||||
KiReadyThread(Thread);
|
KiReadyThread(Thread);
|
||||||
|
@ -251,6 +248,7 @@ KeRemoveQueue(IN PKQUEUE Queue,
|
||||||
BOOLEAN Swappable;
|
BOOLEAN Swappable;
|
||||||
PLARGE_INTEGER OriginalDueTime = Timeout;
|
PLARGE_INTEGER OriginalDueTime = Timeout;
|
||||||
LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
|
LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
|
||||||
|
ULONG Hand = 0;
|
||||||
ASSERT_QUEUE(Queue);
|
ASSERT_QUEUE(Queue);
|
||||||
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
||||||
|
|
||||||
|
@ -390,7 +388,7 @@ KeRemoveQueue(IN PKQUEUE Queue,
|
||||||
if (Timeout)
|
if (Timeout)
|
||||||
{
|
{
|
||||||
/* Insert it */
|
/* Insert it */
|
||||||
KiInsertWaitTimer(Timer);
|
KxInsertTimer(Timer, Hand);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,188 +4,242 @@
|
||||||
* FILE: ntoskrnl/ke/timer.c
|
* FILE: ntoskrnl/ke/timer.c
|
||||||
* PURPOSE: Handle Kernel Timers (Kernel-part of Executive Timers)
|
* PURPOSE: Handle Kernel Timers (Kernel-part of Executive Timers)
|
||||||
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
||||||
* David Welch (welch@mcmail.com)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* INCLUDES ***************************************************************/
|
/* INCLUDES ******************************************************************/
|
||||||
|
|
||||||
#include <ntoskrnl.h>
|
#include <ntoskrnl.h>
|
||||||
#define NDEBUG
|
#define NDEBUG
|
||||||
#include <internal/debug.h>
|
#include <debug.h>
|
||||||
|
|
||||||
/* GLOBALS ****************************************************************/
|
/* GLOBALS *******************************************************************/
|
||||||
|
|
||||||
|
KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE];
|
||||||
LARGE_INTEGER KiTimeIncrementReciprocal;
|
LARGE_INTEGER KiTimeIncrementReciprocal;
|
||||||
UCHAR KiTimeIncrementShiftCount;
|
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
|
BOOLEAN
|
||||||
NTAPI
|
FASTCALL
|
||||||
KiInsertTimer(IN PKTIMER Timer,
|
KiInsertTreeTimer(IN PKTIMER Timer,
|
||||||
IN LARGE_INTEGER DueTime)
|
IN LARGE_INTEGER Interval)
|
||||||
{
|
{
|
||||||
LARGE_INTEGER SystemTime;
|
BOOLEAN Inserted = FALSE;
|
||||||
LARGE_INTEGER DifferenceTime;
|
ULONG Hand = 0;
|
||||||
ULONGLONG InterruptTime;
|
PKSPIN_LOCK_QUEUE LockQueue;
|
||||||
|
LONGLONG DueTime;
|
||||||
/* Set default data */
|
LARGE_INTEGER InterruptTime, SystemTime, DifferenceTime;
|
||||||
Timer->Header.Inserted = TRUE;
|
PKTIMER_TABLE_ENTRY TimerEntry;
|
||||||
Timer->Header.Absolute = FALSE;
|
|
||||||
if (!Timer->Period) Timer->Header.SignalState = FALSE;
|
|
||||||
|
|
||||||
/* Convert to relative time if needed */
|
/* Convert to relative time if needed */
|
||||||
if (DueTime.HighPart >= 0)
|
Timer->Header.Absolute = FALSE;
|
||||||
|
if (Interval.HighPart >= 0)
|
||||||
{
|
{
|
||||||
/* Get System Time */
|
/* Get System Time */
|
||||||
KeQuerySystemTime(&SystemTime);
|
KeQuerySystemTime(&SystemTime);
|
||||||
|
|
||||||
/* Do the conversion */
|
/* Do the conversion */
|
||||||
DifferenceTime.QuadPart = SystemTime.QuadPart - DueTime.QuadPart;
|
DifferenceTime.QuadPart = SystemTime.QuadPart - Interval.QuadPart;
|
||||||
|
|
||||||
/* Make sure it hasn't already expired */
|
/* Make sure it hasn't already expired */
|
||||||
|
Timer->Header.Absolute = TRUE;
|
||||||
if (DifferenceTime.HighPart >= 0)
|
if (DifferenceTime.HighPart >= 0)
|
||||||
{
|
{
|
||||||
/* Cancel everything */
|
/* Cancel everything */
|
||||||
Timer->Header.SignalState = TRUE;
|
Timer->Header.SignalState = TRUE;
|
||||||
Timer->Header.Inserted = FALSE;
|
Timer->Header.Hand = 0;
|
||||||
|
Timer->DueTime.QuadPart = 0;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the time as Absolute */
|
/* Set the time as Absolute */
|
||||||
Timer->Header.Absolute = TRUE;
|
Interval = DifferenceTime;
|
||||||
DueTime = DifferenceTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the Interrupt Time */
|
/* Get the Interrupt Time */
|
||||||
InterruptTime = KeQueryInterruptTime();
|
InterruptTime.QuadPart = KeQueryInterruptTime();
|
||||||
|
|
||||||
/* Set the Final Due Time */
|
/* Recalculate due time */
|
||||||
Timer->DueTime.QuadPart = InterruptTime - DueTime.QuadPart;
|
DueTime = InterruptTime.QuadPart - Interval.QuadPart;
|
||||||
|
Timer->DueTime.QuadPart = DueTime;
|
||||||
|
|
||||||
/* Now insert it into the Timer List */
|
/* Get the handle */
|
||||||
InsertAscendingList(&KiTimerListHead,
|
Hand = KiComputeTimerTableIndex(DueTime);
|
||||||
Timer,
|
Timer->Header.Hand = (UCHAR)Hand;
|
||||||
KTIMER,
|
Timer->Header.Inserted = TRUE;
|
||||||
TimerListEntry,
|
|
||||||
DueTime.QuadPart);
|
/* Acquire the lock */
|
||||||
return TRUE;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
BOOLEAN
|
||||||
* We enter this function at IRQL DISPATCH_LEVEL, and with the
|
FASTCALL
|
||||||
* Dispatcher Lock held!
|
KiInsertTimerTable(IN PKTIMER Timer,
|
||||||
*/
|
IN ULONG Hand)
|
||||||
VOID
|
|
||||||
NTAPI
|
|
||||||
KiHandleExpiredTimer(IN PKTIMER Timer)
|
|
||||||
{
|
{
|
||||||
LARGE_INTEGER DueTime;
|
LARGE_INTEGER InterruptTime;
|
||||||
|
LONGLONG DueTime = Timer->DueTime.QuadPart;
|
||||||
/* Set it as Signaled */
|
BOOLEAN Expired = FALSE;
|
||||||
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;
|
|
||||||
PLIST_ENTRY ListHead, NextEntry;
|
PLIST_ENTRY ListHead, NextEntry;
|
||||||
KIRQL OldIrql;
|
PKTIMER CurrentTimer;
|
||||||
|
|
||||||
/* Initialize the Expired Timer List */
|
/* Check if the period is zero */
|
||||||
InitializeListHead(&ExpiredTimerList);
|
if (!Timer->Period) Timer->Header.SignalState = FALSE;
|
||||||
|
|
||||||
/* Lock the Database and Raise IRQL */
|
/* Sanity check */
|
||||||
OldIrql = KiAcquireDispatcherLock();
|
ASSERT(Hand == KiComputeTimerTableIndex(DueTime));
|
||||||
|
|
||||||
/* Query Interrupt Times */
|
/* Loop the timer list backwards */
|
||||||
InterruptTime = KeQueryInterruptTime();
|
ListHead = &KiTimerTableListHead[Hand].Entry;
|
||||||
|
NextEntry = ListHead->Blink;
|
||||||
/* Loop through the Timer List */
|
|
||||||
ListHead = &KiTimerListHead;
|
|
||||||
NextEntry = ListHead->Flink;
|
|
||||||
while (NextEntry != ListHead)
|
while (NextEntry != ListHead)
|
||||||
{
|
{
|
||||||
/* Get the timer */
|
/* Get the timer */
|
||||||
Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
|
CurrentTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
|
||||||
|
|
||||||
/* Check if we have to Expire it */
|
/* Now check if we can fit it before */
|
||||||
if (InterruptTime < Timer->DueTime.QuadPart) break;
|
if ((ULONGLONG)DueTime >= CurrentTimer->DueTime.QuadPart) break;
|
||||||
|
|
||||||
/* Remove it from the Timer List, add it to the Expired List */
|
/* Keep looping */
|
||||||
RemoveEntryList(&Timer->TimerListEntry);
|
NextEntry = NextEntry->Blink;
|
||||||
InsertTailList(&ExpiredTimerList, &Timer->TimerListEntry);
|
|
||||||
NextEntry = ListHead->Flink;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Expire the Timers */
|
/* Looped all the list, insert it here and get the interrupt time again */
|
||||||
while (ExpiredTimerList.Flink != &ExpiredTimerList)
|
InsertHeadList(NextEntry, &Timer->TimerListEntry);
|
||||||
|
|
||||||
|
/* Check if we didn't find it in the list */
|
||||||
|
if (NextEntry == ListHead)
|
||||||
{
|
{
|
||||||
/* Get the Timer */
|
/* Set the time */
|
||||||
Timer = CONTAINING_RECORD(ExpiredTimerList.Flink,
|
KiTimerTableListHead[Hand].Time.QuadPart = DueTime;
|
||||||
KTIMER,
|
|
||||||
TimerListEntry);
|
|
||||||
|
|
||||||
/* Remove it */
|
/* Make sure it hasn't expired already */
|
||||||
///
|
InterruptTime.QuadPart = KeQueryInterruptTime();
|
||||||
// GCC IS A BRAINDEAD PIECE OF SHIT. WILL GIVE 5$ FOR EACH DEV KILLED.
|
if (DueTime <= InterruptTime.QuadPart) Expired = TRUE;
|
||||||
///
|
|
||||||
Timer->Header.Inserted = FALSE;
|
|
||||||
RemoveEntryList(&Timer->TimerListEntry);
|
|
||||||
//KiRemoveTimer(Timer);
|
|
||||||
|
|
||||||
/* Expire it */
|
|
||||||
KiHandleExpiredTimer(Timer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Release Dispatcher Lock */
|
/* Return expired state */
|
||||||
KiReleaseDispatcherLock(OldIrql);
|
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 **********************************************************/
|
/* PUBLIC FUNCTIONS **********************************************************/
|
||||||
|
@ -200,25 +254,19 @@ KeCancelTimer(IN OUT PKTIMER Timer)
|
||||||
KIRQL OldIrql;
|
KIRQL OldIrql;
|
||||||
BOOLEAN Inserted;
|
BOOLEAN Inserted;
|
||||||
ASSERT_TIMER(Timer);
|
ASSERT_TIMER(Timer);
|
||||||
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
||||||
|
|
||||||
/* Lock the Database and Raise IRQL */
|
/* Lock the Database and Raise IRQL */
|
||||||
OldIrql = KiAcquireDispatcherLock();
|
OldIrql = KiAcquireDispatcherLock();
|
||||||
|
|
||||||
/* Check if it's inserted, and remove it if it is */
|
/* Check if it's inserted, and remove it if it is */
|
||||||
Inserted = Timer->Header.Inserted;
|
Inserted = Timer->Header.Inserted;
|
||||||
if (Inserted)
|
if (Inserted) KxRemoveTreeTimer(Timer);
|
||||||
{
|
|
||||||
///
|
|
||||||
// GCC IS A BRAINDEAD PIECE OF SHIT. WILL GIVE 5$ FOR EACH DEV KILLED.
|
|
||||||
///
|
|
||||||
Timer->Header.Inserted = FALSE;
|
|
||||||
RemoveEntryList(&Timer->TimerListEntry);
|
|
||||||
//KiRemoveTimer(Timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Release Dispatcher Lock */
|
/* Release Dispatcher Lock */
|
||||||
KiReleaseDispatcherLock(OldIrql);
|
KiReleaseDispatcherLock(OldIrql);
|
||||||
|
|
||||||
|
/* Return the old state */
|
||||||
return Inserted;
|
return Inserted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,62 +337,75 @@ KeSetTimerEx(IN OUT PKTIMER Timer,
|
||||||
{
|
{
|
||||||
KIRQL OldIrql;
|
KIRQL OldIrql;
|
||||||
BOOLEAN Inserted;
|
BOOLEAN Inserted;
|
||||||
|
ULONG Hand = 0;
|
||||||
|
LARGE_INTEGER InterruptTime, SystemTime, DifferenceTime;
|
||||||
|
BOOLEAN RequestInterrupt = FALSE;
|
||||||
ASSERT_TIMER(Timer);
|
ASSERT_TIMER(Timer);
|
||||||
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
||||||
|
|
||||||
/* Lock the Database and Raise IRQL */
|
/* Lock the Database and Raise IRQL */
|
||||||
OldIrql = KiAcquireDispatcherLock();
|
OldIrql = KiAcquireDispatcherLock();
|
||||||
|
|
||||||
/* Check if it's inserted, and remove it if it is */
|
/* Check if it's inserted, and remove it if it is */
|
||||||
Inserted = Timer->Header.Inserted;
|
Inserted = Timer->Header.Inserted;
|
||||||
if (Inserted)
|
if (Inserted) KxRemoveTreeTimer(Timer);
|
||||||
{
|
|
||||||
///
|
|
||||||
// GCC IS A BRAINDEAD PIECE OF SHIT. WILL GIVE 5$ FOR EACH DEV KILLED.
|
|
||||||
///
|
|
||||||
Timer->Header.Inserted = FALSE;
|
|
||||||
RemoveEntryList(&Timer->TimerListEntry);
|
|
||||||
//KiRemoveTimer(Timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set Default Timer Data */
|
/* Set Default Timer Data */
|
||||||
Timer->Dpc = Dpc;
|
Timer->Dpc = Dpc;
|
||||||
Timer->Period = Period;
|
Timer->Period = Period;
|
||||||
Timer->Header.SignalState = FALSE;
|
|
||||||
|
|
||||||
/* Insert it */
|
/* Convert to relative time if needed */
|
||||||
if (!KiInsertTimer(Timer, DueTime))
|
Timer->Header.Absolute = FALSE;
|
||||||
|
if (DueTime.HighPart >= 0)
|
||||||
{
|
{
|
||||||
/* Check if it has any waiters */
|
/* Get System Time */
|
||||||
if (!IsListEmpty(&Timer->Header.WaitListHead))
|
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 */
|
/* Cancel everything */
|
||||||
KiWaitTest(Timer, IO_NO_INCREMENT);
|
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 */
|
/* Set the time as Absolute */
|
||||||
if (Dpc)
|
DueTime = DifferenceTime;
|
||||||
{
|
|
||||||
/* 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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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 */
|
/* Release Dispatcher Lock */
|
||||||
KiReleaseDispatcherLock(OldIrql);
|
KiExitDispatcher(OldIrql);
|
||||||
|
|
||||||
/* Return old state */
|
/* Return old state */
|
||||||
return Inserted;
|
return Inserted;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* EOF */
|
|
||||||
|
|
|
@ -77,13 +77,7 @@ KiUnlinkThread(IN PKTHREAD Thread,
|
||||||
|
|
||||||
/* Check if there's a Thread Timer */
|
/* Check if there's a Thread Timer */
|
||||||
Timer = &Thread->Timer;
|
Timer = &Thread->Timer;
|
||||||
if (Timer->Header.Inserted)
|
if (Timer->Header.Inserted) KxRemoveTreeTimer(Timer);
|
||||||
{
|
|
||||||
/* Remove the timer */
|
|
||||||
Timer->Header.Inserted = FALSE;
|
|
||||||
RemoveEntryList(&Timer->TimerListEntry);
|
|
||||||
//KiRemoveTimer(Timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Increment the Queue's active threads */
|
/* Increment the Queue's active threads */
|
||||||
if (Thread->Queue) Thread->Queue->CurrentCount++;
|
if (Thread->Queue) Thread->Queue->CurrentCount++;
|
||||||
|
@ -219,6 +213,7 @@ KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode,
|
||||||
BOOLEAN Swappable;
|
BOOLEAN Swappable;
|
||||||
PLARGE_INTEGER OriginalDueTime;
|
PLARGE_INTEGER OriginalDueTime;
|
||||||
LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
|
LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
|
||||||
|
ULONG Hand = 0;
|
||||||
|
|
||||||
/* If this is a user-mode wait of 0 seconds, yield execution */
|
/* If this is a user-mode wait of 0 seconds, yield execution */
|
||||||
if (!(Interval->QuadPart) && (WaitMode != KernelMode))
|
if (!(Interval->QuadPart) && (WaitMode != KernelMode))
|
||||||
|
@ -285,7 +280,7 @@ KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode,
|
||||||
/* Insert the timer and swap the thread */
|
/* Insert the timer and swap the thread */
|
||||||
ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
|
ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
|
||||||
KiSetThreadSwapBusy(Thread);
|
KiSetThreadSwapBusy(Thread);
|
||||||
KiInsertWaitTimer(Timer);
|
KxInsertTimer(Timer, Hand);
|
||||||
WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb());
|
WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb());
|
||||||
|
|
||||||
/* Check if were swapped ok */
|
/* Check if were swapped ok */
|
||||||
|
@ -350,6 +345,7 @@ KeWaitForSingleObject(IN PVOID Object,
|
||||||
BOOLEAN Swappable;
|
BOOLEAN Swappable;
|
||||||
LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
|
LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
|
||||||
PLARGE_INTEGER OriginalDueTime = Timeout;
|
PLARGE_INTEGER OriginalDueTime = Timeout;
|
||||||
|
ULONG Hand = 0;
|
||||||
|
|
||||||
/* Check if the lock is already held */
|
/* Check if the lock is already held */
|
||||||
if (!Thread->WaitNext) goto WaitStart;
|
if (!Thread->WaitNext) goto WaitStart;
|
||||||
|
@ -449,7 +445,7 @@ KeWaitForSingleObject(IN PVOID Object,
|
||||||
if (Timeout)
|
if (Timeout)
|
||||||
{
|
{
|
||||||
/* Insert it */
|
/* Insert it */
|
||||||
KiInsertWaitTimer(Timer);
|
KxInsertTimer(Timer, Hand);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -515,7 +511,7 @@ KeWaitForMultipleObjects(IN ULONG Count,
|
||||||
BOOLEAN Swappable;
|
BOOLEAN Swappable;
|
||||||
PLARGE_INTEGER OriginalDueTime = Timeout;
|
PLARGE_INTEGER OriginalDueTime = Timeout;
|
||||||
LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
|
LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
|
||||||
ULONG Index;
|
ULONG Index, Hand = 0;
|
||||||
|
|
||||||
/* Make sure the Wait Count is valid */
|
/* Make sure the Wait Count is valid */
|
||||||
if (!WaitBlockArray)
|
if (!WaitBlockArray)
|
||||||
|
@ -726,7 +722,7 @@ KeWaitForMultipleObjects(IN ULONG Count,
|
||||||
if (Timeout)
|
if (Timeout)
|
||||||
{
|
{
|
||||||
/* Insert it */
|
/* Insert it */
|
||||||
KiInsertWaitTimer(Timer);
|
KxInsertTimer(Timer, Hand);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue