From f1e71db26d48ce9f3a41dc97454b6d036e6b3375 Mon Sep 17 00:00:00 2001 From: Alex Ionescu Date: Wed, 24 Jan 2007 06:50:28 +0000 Subject: [PATCH] - 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 --- reactos/ntoskrnl/KrnlFun.c | 18 +- reactos/ntoskrnl/include/internal/ke.h | 55 +- reactos/ntoskrnl/include/internal/ke_x.h | 1087 ++++++++++++---------- reactos/ntoskrnl/ke/clock.c | 3 +- reactos/ntoskrnl/ke/dpc.c | 243 ++++- reactos/ntoskrnl/ke/i386/clock.S | 86 +- reactos/ntoskrnl/ke/krnlinit.c | 9 +- reactos/ntoskrnl/ke/queue.c | 12 +- reactos/ntoskrnl/ke/timer.c | 409 ++++---- reactos/ntoskrnl/ke/wait.c | 18 +- 10 files changed, 1192 insertions(+), 748 deletions(-) diff --git a/reactos/ntoskrnl/KrnlFun.c b/reactos/ntoskrnl/KrnlFun.c index 5f27d66c918..3288f8b742e 100644 --- a/reactos/ntoskrnl/KrnlFun.c +++ b/reactos/ntoskrnl/KrnlFun.c @@ -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| // | || || || || || || || || || || || || | | diff --git a/reactos/ntoskrnl/include/internal/ke.h b/reactos/ntoskrnl/include/internal/ke.h index 9e213f6f327..3d079faa3ea 100644 --- a/reactos/ntoskrnl/include/internal/ke.h +++ b/reactos/ntoskrnl/include/internal/ke.h @@ -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( diff --git a/reactos/ntoskrnl/include/internal/ke_x.h b/reactos/ntoskrnl/include/internal/ke_x.h index 11050ad723a..0a354218c11 100644 --- a/reactos/ntoskrnl/include/internal/ke_x.h +++ b/reactos/ntoskrnl/include/internal/ke_x.h @@ -175,509 +175,6 @@ Ke386SanitizeDr(IN PVOID DrAddress, } \ } -// -// Satisfies the wait of any dispatcher object -// -#define KiSatisfyObjectWait(Object, Thread) \ -{ \ - /* Special case for Mutants */ \ - if ((Object)->Header.Type == MutantObject) \ - { \ - /* Decrease the Signal State */ \ - (Object)->Header.SignalState--; \ - \ - /* Check if it's now non-signaled */ \ - if (!(Object)->Header.SignalState) \ - { \ - /* Set the Owner Thread */ \ - (Object)->OwnerThread = Thread; \ - \ - /* Disable APCs if needed */ \ - Thread->KernelApcDisable = Thread->KernelApcDisable - \ - (Object)->ApcDisable; \ - \ - /* Check if it's abandoned */ \ - if ((Object)->Abandoned) \ - { \ - /* Unabandon it */ \ - (Object)->Abandoned = FALSE; \ - \ - /* Return Status */ \ - Thread->WaitStatus = STATUS_ABANDONED; \ - } \ - \ - /* Insert it into the Mutant List */ \ - InsertHeadList(Thread->MutantListHead.Blink, \ - &(Object)->MutantListEntry); \ - } \ - } \ - else if (((Object)->Header.Type & TIMER_OR_EVENT_TYPE) == \ - EventSynchronizationObject) \ - { \ - /* Synchronization Timers and Events just get un-signaled */ \ - (Object)->Header.SignalState = 0; \ - } \ - else if ((Object)->Header.Type == SemaphoreObject) \ - { \ - /* These ones can have multiple states, so we only decrease it */ \ - (Object)->Header.SignalState--; \ - } \ -} - -// -// Satisfies the wait of a mutant dispatcher object -// -#define KiSatisfyMutantWait(Object, Thread) \ -{ \ - /* Decrease the Signal State */ \ - (Object)->Header.SignalState--; \ - \ - /* Check if it's now non-signaled */ \ - if (!(Object)->Header.SignalState) \ - { \ - /* Set the Owner Thread */ \ - (Object)->OwnerThread = Thread; \ - \ - /* Disable APCs if needed */ \ - Thread->KernelApcDisable = Thread->KernelApcDisable - \ - (Object)->ApcDisable; \ - \ - /* Check if it's abandoned */ \ - if ((Object)->Abandoned) \ - { \ - /* Unabandon it */ \ - (Object)->Abandoned = FALSE; \ - \ - /* Return Status */ \ - Thread->WaitStatus = STATUS_ABANDONED; \ - } \ - \ - /* Insert it into the Mutant List */ \ - InsertHeadList(Thread->MutantListHead.Blink, \ - &(Object)->MutantListEntry); \ - } \ -} - -// -// Satisfies the wait of any nonmutant dispatcher object -// -#define KiSatisfyNonMutantWait(Object) \ -{ \ - if (((Object)->Header.Type & TIMER_OR_EVENT_TYPE) == \ - EventSynchronizationObject) \ - { \ - /* Synchronization Timers and Events just get un-signaled */ \ - (Object)->Header.SignalState = 0; \ - } \ - else if ((Object)->Header.Type == SemaphoreObject) \ - { \ - /* These ones can have multiple states, so we only decrease it */ \ - (Object)->Header.SignalState--; \ - } \ -} - -// -// Recalculates the due time -// -PLARGE_INTEGER -FORCEINLINE -KiRecalculateDueTime(IN PLARGE_INTEGER OriginalDueTime, - IN PLARGE_INTEGER DueTime, - IN OUT PLARGE_INTEGER NewDueTime) -{ - /* Don't do anything for absolute waits */ - if (OriginalDueTime->QuadPart >= 0) return OriginalDueTime; - - /* Otherwise, query the interrupt time and recalculate */ - NewDueTime->QuadPart = KeQueryInterruptTime(); - NewDueTime->QuadPart -= DueTime->QuadPart; - return NewDueTime; -} - -// -// Determines wether a thread should be added to the wait list -// -FORCEINLINE -BOOLEAN -KiCheckThreadStackSwap(IN PKTHREAD Thread, - IN KPROCESSOR_MODE WaitMode) -{ - /* Check the required conditions */ - if ((WaitMode != KernelMode) && - (Thread->EnableStackSwap) && - (Thread->Priority >= (LOW_REALTIME_PRIORITY + 9))) - { - /* We are go for swap */ - return TRUE; - } - else - { - /* Don't swap the thread */ - return FALSE; - } -} - -// -// Adds a thread to the wait list -// -#define KiAddThreadToWaitList(Thread, Swappable) \ -{ \ - /* Make sure it's swappable */ \ - if (Swappable) \ - { \ - /* Insert it into the PRCB's List */ \ - InsertTailList(&KeGetCurrentPrcb()->WaitListHead, \ - &Thread->WaitListEntry); \ - } \ -} - -// -// Checks if a wait in progress should be interrupted by APCs or an alertable -// state. -// -FORCEINLINE -NTSTATUS -KiCheckAlertability(IN PKTHREAD Thread, - IN BOOLEAN Alertable, - IN KPROCESSOR_MODE WaitMode) -{ - /* Check if the wait is alertable */ - if (Alertable) - { - /* It is, first check if the thread is alerted in this mode */ - if (Thread->Alerted[WaitMode]) - { - /* It is, so bail out of the wait */ - Thread->Alerted[WaitMode] = FALSE; - return STATUS_ALERTED; - } - else if ((WaitMode != KernelMode) && - (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))) - { - /* It's isn't, but this is a user wait with queued user APCs */ - Thread->ApcState.UserApcPending = TRUE; - return STATUS_USER_APC; - } - else if (Thread->Alerted[KernelMode]) - { - /* It isn't that either, but we're alered in kernel mode */ - Thread->Alerted[KernelMode] = FALSE; - return STATUS_ALERTED; - } - } - else if ((WaitMode != KernelMode) && (Thread->ApcState.UserApcPending)) - { - /* Not alertable, but this is a user wait with pending user APCs */ - return STATUS_USER_APC; - } - - /* Otherwise, we're fine */ - return STATUS_WAIT_0; -} - -VOID -FORCEINLINE -KxSetTimerForThreadWait(IN PKTIMER Timer, - IN LARGE_INTEGER Interval) -{ - LARGE_INTEGER InterruptTime, SystemTime, TimeDifference; - - /* Check the timer's interval to see if it's absolute */ - Timer->Header.Absolute = FALSE; - if (!Timer->Period) Timer->Header.SignalState = FALSE; - if (Interval.HighPart >= 0) - { - /* Get the system time and calculate the relative time */ - KeQuerySystemTime(&SystemTime); - TimeDifference.QuadPart = SystemTime.QuadPart - Interval.QuadPart; - Timer->Header.Absolute = TRUE; - - /* Check if we've already expired */ - if (TimeDifference.HighPart >= 0) - { - /* Reset everything */ - Timer->DueTime.QuadPart = 0; - Timer->Header.SignalState = TRUE; - return; - } - else - { - /* Update the interval */ - Interval = TimeDifference; - } - } - - /* Calculate the due time */ - InterruptTime.QuadPart = KeQueryInterruptTime(); - Timer->DueTime.QuadPart = InterruptTime.QuadPart - Interval.QuadPart; -} - -#define KxDelayThreadWait() \ - \ - /* Setup the Wait Block */ \ - Thread->WaitBlockList = TimerBlock; \ - \ - /* Setup the timer */ \ - KxSetTimerForThreadWait(Timer, *Interval); \ - \ - /* Save the due time for the caller */ \ - DueTime.QuadPart = Timer->DueTime.QuadPart; \ - \ - /* Link the timer to this Wait Block */ \ - TimerBlock->NextWaitBlock = TimerBlock; \ - Timer->Header.WaitListHead.Flink = &TimerBlock->WaitListEntry; \ - Timer->Header.WaitListHead.Blink = &TimerBlock->WaitListEntry; \ - \ - /* Clear wait status */ \ - Thread->WaitStatus = STATUS_SUCCESS; \ - \ - /* Setup wait fields */ \ - Thread->Alertable = Alertable; \ - Thread->WaitReason = DelayExecution; \ - Thread->WaitMode = WaitMode; \ - \ - /* Check if we can swap the thread's stack */ \ - Thread->WaitListEntry.Flink = NULL; \ - Swappable = KiCheckThreadStackSwap(Thread, WaitMode); \ - \ - /* Set the wait time */ \ - Thread->WaitTime = KeTickCount.LowPart; - -#define KxMultiThreadWait() \ - /* Link wait block array to the thread */ \ - Thread->WaitBlockList = WaitBlockArray; \ - \ - /* Reset the index */ \ - Index = 0; \ - \ - /* Loop wait blocks */ \ - do \ - { \ - /* Fill out the wait block */ \ - WaitBlock = &WaitBlockArray[Index]; \ - WaitBlock->Object = Object[Index]; \ - WaitBlock->WaitKey = (USHORT)Index; \ - WaitBlock->WaitType = WaitType; \ - WaitBlock->Thread = Thread; \ - \ - /* Link to next block */ \ - WaitBlock->NextWaitBlock = &WaitBlockArray[Index + 1]; \ - Index++; \ - } while (Index < Count); \ - \ - /* Link the last block */ \ - WaitBlock->NextWaitBlock = WaitBlockArray; \ - \ - /* Set default wait status */ \ - Thread->WaitStatus = STATUS_WAIT_0; \ - \ - /* Check if we have a timer */ \ - if (Timeout) \ - { \ - /* Link to the block */ \ - TimerBlock->NextWaitBlock = WaitBlockArray; \ - \ - /* Setup the timer */ \ - KxSetTimerForThreadWait(Timer, *Timeout); \ - \ - /* Save the due time for the caller */ \ - DueTime.QuadPart = Timer->DueTime.QuadPart; \ - \ - /* Initialize the list */ \ - InitializeListHead(&Timer->Header.WaitListHead); \ - } \ - \ - /* Set wait settings */ \ - Thread->Alertable = Alertable; \ - Thread->WaitMode = WaitMode; \ - Thread->WaitReason = WaitReason; \ - \ - /* Check if we can swap the thread's stack */ \ - Thread->WaitListEntry.Flink = NULL; \ - Swappable = KiCheckThreadStackSwap(Thread, WaitMode); \ - \ - /* Set the wait time */ \ - Thread->WaitTime = KeTickCount.LowPart; - -#define KxSingleThreadWait() \ - /* Setup the Wait Block */ \ - Thread->WaitBlockList = WaitBlock; \ - WaitBlock->WaitKey = STATUS_SUCCESS; \ - WaitBlock->Object = Object; \ - WaitBlock->WaitType = WaitAny; \ - \ - /* Clear wait status */ \ - Thread->WaitStatus = STATUS_SUCCESS; \ - \ - /* Check if we have a timer */ \ - if (Timeout) \ - { \ - /* Setup the timer */ \ - KxSetTimerForThreadWait(Timer, *Timeout); \ - \ - /* Save the due time for the caller */ \ - DueTime.QuadPart = Timer->DueTime.QuadPart; \ - \ - /* Pointer to timer block */ \ - WaitBlock->NextWaitBlock = TimerBlock; \ - TimerBlock->NextWaitBlock = WaitBlock; \ - \ - /* Link the timer to this Wait Block */ \ - Timer->Header.WaitListHead.Flink = &TimerBlock->WaitListEntry; \ - Timer->Header.WaitListHead.Blink = &TimerBlock->WaitListEntry; \ - } \ - else \ - { \ - /* No timer block, just ourselves */ \ - WaitBlock->NextWaitBlock = WaitBlock; \ - } \ - \ - /* Set wait settings */ \ - Thread->Alertable = Alertable; \ - Thread->WaitMode = WaitMode; \ - Thread->WaitReason = WaitReason; \ - \ - /* Check if we can swap the thread's stack */ \ - Thread->WaitListEntry.Flink = NULL; \ - Swappable = KiCheckThreadStackSwap(Thread, WaitMode); \ - \ - /* Set the wait time */ \ - Thread->WaitTime = KeTickCount.LowPart; - -#define KxQueueThreadWait() \ - /* Setup the Wait Block */ \ - Thread->WaitBlockList = WaitBlock; \ - WaitBlock->WaitKey = STATUS_SUCCESS; \ - WaitBlock->Object = Queue; \ - WaitBlock->WaitType = WaitAny; \ - WaitBlock->Thread = Thread; \ - \ - /* Clear wait status */ \ - Thread->WaitStatus = STATUS_SUCCESS; \ - \ - /* Check if we have a timer */ \ - if (Timeout) \ - { \ - /* Setup the timer */ \ - KxSetTimerForThreadWait(Timer, *Timeout); \ - \ - /* Save the due time for the caller */ \ - DueTime.QuadPart = Timer->DueTime.QuadPart; \ - \ - /* Pointer to timer block */ \ - WaitBlock->NextWaitBlock = TimerBlock; \ - TimerBlock->NextWaitBlock = WaitBlock; \ - \ - /* Link the timer to this Wait Block */ \ - Timer->Header.WaitListHead.Flink = &TimerBlock->WaitListEntry; \ - Timer->Header.WaitListHead.Blink = &TimerBlock->WaitListEntry; \ - } \ - else \ - { \ - /* No timer block, just ourselves */ \ - WaitBlock->NextWaitBlock = WaitBlock; \ - } \ - \ - /* Set wait settings */ \ - Thread->Alertable = FALSE; \ - Thread->WaitMode = WaitMode; \ - Thread->WaitReason = WrQueue; \ - \ - /* Check if we can swap the thread's stack */ \ - Thread->WaitListEntry.Flink = NULL; \ - Swappable = KiCheckThreadStackSwap(Thread, WaitMode); \ - \ - /* Set the wait time */ \ - Thread->WaitTime = KeTickCount.LowPart; - -// -// Unwaits a Thread -// -FORCEINLINE -VOID -KxUnwaitThread(IN DISPATCHER_HEADER *Object, - IN KPRIORITY Increment) -{ - PLIST_ENTRY WaitEntry, WaitList; - PKWAIT_BLOCK WaitBlock; - PKTHREAD WaitThread; - ULONG WaitKey; - - /* Loop the Wait Entries */ - WaitList = &Object->WaitListHead; - ASSERT(IsListEmpty(&Object->WaitListHead) == FALSE); - WaitEntry = WaitList->Flink; - do - { - /* Get the current wait block */ - WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry); - - /* Get the waiting thread */ - WaitThread = WaitBlock->Thread; - - /* Check the current Wait Mode */ - if (WaitBlock->WaitType == WaitAny) - { - /* Use the actual wait key */ - WaitKey = WaitBlock->WaitKey; - } - else - { - /* Otherwise, use STATUS_KERNEL_APC */ - WaitKey = STATUS_KERNEL_APC; - } - - /* Unwait the thread */ - KiUnwaitThread(WaitThread, WaitKey, Increment); - - /* Next entry */ - WaitEntry = WaitList->Flink; - } while (WaitEntry != WaitList); -} - -// -// Unwaits a Thread waiting on an event -// -FORCEINLINE -VOID -KxUnwaitThreadForEvent(IN PKEVENT Event, - IN KPRIORITY Increment) -{ - PLIST_ENTRY WaitEntry, WaitList; - PKWAIT_BLOCK WaitBlock; - PKTHREAD WaitThread; - - /* Loop the Wait Entries */ - WaitList = &Event->Header.WaitListHead; - ASSERT(IsListEmpty(&Event->Header.WaitListHead) == FALSE); - WaitEntry = WaitList->Flink; - do - { - /* Get the current wait block */ - WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry); - - /* Get the waiting thread */ - WaitThread = WaitBlock->Thread; - - /* Check the current Wait Mode */ - if (WaitBlock->WaitType == WaitAny) - { - /* Un-signal it */ - Event->Header.SignalState = 0; - - /* Un-signal the event and unwait the thread */ - KiUnwaitThread(WaitThread, WaitBlock->WaitKey, Increment); - break; - } - - /* Unwait the thread with STATUS_KERNEL_APC */ - KiUnwaitThread(WaitThread, STATUS_KERNEL_APC, Increment); - - /* Next entry */ - WaitEntry = WaitList->Flink; - } while (WaitEntry != WaitList); -} - #ifndef _CONFIG_SMP // // Spinlock Acquire at IRQL >= DISPATCH_LEVEL @@ -1296,6 +793,579 @@ KiReleaseDeviceQueueLock(IN PKLOCK_QUEUE_HANDLE DeviceLock) } } +// +// Satisfies the wait of any dispatcher object +// +#define KiSatisfyObjectWait(Object, Thread) \ +{ \ + /* Special case for Mutants */ \ + if ((Object)->Header.Type == MutantObject) \ + { \ + /* Decrease the Signal State */ \ + (Object)->Header.SignalState--; \ + \ + /* Check if it's now non-signaled */ \ + if (!(Object)->Header.SignalState) \ + { \ + /* Set the Owner Thread */ \ + (Object)->OwnerThread = Thread; \ + \ + /* Disable APCs if needed */ \ + Thread->KernelApcDisable = Thread->KernelApcDisable - \ + (Object)->ApcDisable; \ + \ + /* Check if it's abandoned */ \ + if ((Object)->Abandoned) \ + { \ + /* Unabandon it */ \ + (Object)->Abandoned = FALSE; \ + \ + /* Return Status */ \ + Thread->WaitStatus = STATUS_ABANDONED; \ + } \ + \ + /* Insert it into the Mutant List */ \ + InsertHeadList(Thread->MutantListHead.Blink, \ + &(Object)->MutantListEntry); \ + } \ + } \ + else if (((Object)->Header.Type & TIMER_OR_EVENT_TYPE) == \ + EventSynchronizationObject) \ + { \ + /* Synchronization Timers and Events just get un-signaled */ \ + (Object)->Header.SignalState = 0; \ + } \ + else if ((Object)->Header.Type == SemaphoreObject) \ + { \ + /* These ones can have multiple states, so we only decrease it */ \ + (Object)->Header.SignalState--; \ + } \ +} + +// +// Satisfies the wait of a mutant dispatcher object +// +#define KiSatisfyMutantWait(Object, Thread) \ +{ \ + /* Decrease the Signal State */ \ + (Object)->Header.SignalState--; \ + \ + /* Check if it's now non-signaled */ \ + if (!(Object)->Header.SignalState) \ + { \ + /* Set the Owner Thread */ \ + (Object)->OwnerThread = Thread; \ + \ + /* Disable APCs if needed */ \ + Thread->KernelApcDisable = Thread->KernelApcDisable - \ + (Object)->ApcDisable; \ + \ + /* Check if it's abandoned */ \ + if ((Object)->Abandoned) \ + { \ + /* Unabandon it */ \ + (Object)->Abandoned = FALSE; \ + \ + /* Return Status */ \ + Thread->WaitStatus = STATUS_ABANDONED; \ + } \ + \ + /* Insert it into the Mutant List */ \ + InsertHeadList(Thread->MutantListHead.Blink, \ + &(Object)->MutantListEntry); \ + } \ +} + +// +// Satisfies the wait of any nonmutant dispatcher object +// +#define KiSatisfyNonMutantWait(Object) \ +{ \ + if (((Object)->Header.Type & TIMER_OR_EVENT_TYPE) == \ + EventSynchronizationObject) \ + { \ + /* Synchronization Timers and Events just get un-signaled */ \ + (Object)->Header.SignalState = 0; \ + } \ + else if ((Object)->Header.Type == SemaphoreObject) \ + { \ + /* These ones can have multiple states, so we only decrease it */ \ + (Object)->Header.SignalState--; \ + } \ +} + +// +// Recalculates the due time +// +PLARGE_INTEGER +FORCEINLINE +KiRecalculateDueTime(IN PLARGE_INTEGER OriginalDueTime, + IN PLARGE_INTEGER DueTime, + IN OUT PLARGE_INTEGER NewDueTime) +{ + /* Don't do anything for absolute waits */ + if (OriginalDueTime->QuadPart >= 0) return OriginalDueTime; + + /* Otherwise, query the interrupt time and recalculate */ + NewDueTime->QuadPart = KeQueryInterruptTime(); + NewDueTime->QuadPart -= DueTime->QuadPart; + return NewDueTime; +} + +// +// Determines whether a thread should be added to the wait list +// +FORCEINLINE +BOOLEAN +KiCheckThreadStackSwap(IN PKTHREAD Thread, + IN KPROCESSOR_MODE WaitMode) +{ + /* Check the required conditions */ + if ((WaitMode != KernelMode) && + (Thread->EnableStackSwap) && + (Thread->Priority >= (LOW_REALTIME_PRIORITY + 9))) + { + /* We are go for swap */ + return TRUE; + } + else + { + /* Don't swap the thread */ + return FALSE; + } +} + +// +// Adds a thread to the wait list +// +#define KiAddThreadToWaitList(Thread, Swappable) \ +{ \ + /* Make sure it's swappable */ \ + if (Swappable) \ + { \ + /* Insert it into the PRCB's List */ \ + InsertTailList(&KeGetCurrentPrcb()->WaitListHead, \ + &Thread->WaitListEntry); \ + } \ +} + +// +// Checks if a wait in progress should be interrupted by APCs or an alertable +// state. +// +FORCEINLINE +NTSTATUS +KiCheckAlertability(IN PKTHREAD Thread, + IN BOOLEAN Alertable, + IN KPROCESSOR_MODE WaitMode) +{ + /* Check if the wait is alertable */ + if (Alertable) + { + /* It is, first check if the thread is alerted in this mode */ + if (Thread->Alerted[WaitMode]) + { + /* It is, so bail out of the wait */ + Thread->Alerted[WaitMode] = FALSE; + return STATUS_ALERTED; + } + else if ((WaitMode != KernelMode) && + (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))) + { + /* It's isn't, but this is a user wait with queued user APCs */ + Thread->ApcState.UserApcPending = TRUE; + return STATUS_USER_APC; + } + else if (Thread->Alerted[KernelMode]) + { + /* It isn't that either, but we're alered in kernel mode */ + Thread->Alerted[KernelMode] = FALSE; + return STATUS_ALERTED; + } + } + else if ((WaitMode != KernelMode) && (Thread->ApcState.UserApcPending)) + { + /* Not alertable, but this is a user wait with pending user APCs */ + return STATUS_USER_APC; + } + + /* Otherwise, we're fine */ + return STATUS_WAIT_0; +} + +// +// Called by Wait and Queue code to insert a timer for dispatching. +// Also called by KeSetTimerEx to insert a timer from the caller. +// +VOID +FORCEINLINE +KxInsertTimer(IN PKTIMER Timer, + IN ULONG Hand) +{ + PKSPIN_LOCK_QUEUE LockQueue; + + /* Acquire the lock and release the dispatcher lock */ + LockQueue = KiAcquireTimerLock(Hand); + KiReleaseDispatcherLockFromDpcLevel(); + + /* Try to insert the timer */ + if (KiInsertTimerTable(Timer, Hand)) + { + /* Complete it */ + KiCompleteTimer(Timer, LockQueue); + } + else + { + /* Do nothing, just release the lock */ + KiReleaseTimerLock(LockQueue); + } +} + +// +// Called from Unlink and Queue Insert Code. +// Also called by timer code when canceling an inserted timer. +// Removes a timer from it's tree. +// +VOID +FORCEINLINE +KxRemoveTreeTimer(IN PKTIMER Timer) +{ + ULONG Hand = Timer->Header.Hand; + PKSPIN_LOCK_QUEUE LockQueue; + PKTIMER_TABLE_ENTRY TimerEntry; + + /* Acquire timer lock */ + LockQueue = KiAcquireTimerLock(Hand); + + /* Set the timer as non-inserted */ + Timer->Header.Inserted = FALSE; + + /* Remove it from the timer list */ + 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; + } + } + + /* Release the timer lock */ + KiReleaseTimerLock(LockQueue); +} + +VOID +FORCEINLINE +KxSetTimerForThreadWait(IN PKTIMER Timer, + IN LARGE_INTEGER Interval, + OUT PULONG Hand) +{ + ULONGLONG DueTime; + LARGE_INTEGER InterruptTime, SystemTime, TimeDifference; + + /* Check the timer's interval to see if it's absolute */ + Timer->Header.Absolute = FALSE; + if (Interval.HighPart >= 0) + { + /* Get the system time and calculate the relative time */ + KeQuerySystemTime(&SystemTime); + TimeDifference.QuadPart = SystemTime.QuadPart - Interval.QuadPart; + Timer->Header.Absolute = TRUE; + + /* Check if we've already expired */ + if (TimeDifference.HighPart >= 0) + { + /* Reset everything */ + Timer->DueTime.QuadPart = 0; + *Hand = 0; + Timer->Header.Hand = 0; + return; + } + else + { + /* Update the interval */ + Interval = TimeDifference; + } + } + + /* Calculate the due time */ + InterruptTime.QuadPart = KeQueryInterruptTime(); + DueTime = InterruptTime.QuadPart - Interval.QuadPart; + Timer->DueTime.QuadPart = DueTime; + + /* Calculate the timer handle */ + *Hand = KiComputeTimerTableIndex(DueTime); + Timer->Header.Hand = (UCHAR)*Hand; +} + +#define KxDelayThreadWait() \ + \ + /* Setup the Wait Block */ \ + Thread->WaitBlockList = TimerBlock; \ + \ + /* Setup the timer */ \ + KxSetTimerForThreadWait(Timer, *Interval, &Hand); \ + \ + /* Save the due time for the caller */ \ + DueTime.QuadPart = Timer->DueTime.QuadPart; \ + \ + /* Link the timer to this Wait Block */ \ + TimerBlock->NextWaitBlock = TimerBlock; \ + Timer->Header.WaitListHead.Flink = &TimerBlock->WaitListEntry; \ + Timer->Header.WaitListHead.Blink = &TimerBlock->WaitListEntry; \ + \ + /* Clear wait status */ \ + Thread->WaitStatus = STATUS_SUCCESS; \ + \ + /* Setup wait fields */ \ + Thread->Alertable = Alertable; \ + Thread->WaitReason = DelayExecution; \ + Thread->WaitMode = WaitMode; \ + \ + /* Check if we can swap the thread's stack */ \ + Thread->WaitListEntry.Flink = NULL; \ + Swappable = KiCheckThreadStackSwap(Thread, WaitMode); \ + \ + /* Set the wait time */ \ + Thread->WaitTime = KeTickCount.LowPart; + +#define KxMultiThreadWait() \ + /* Link wait block array to the thread */ \ + Thread->WaitBlockList = WaitBlockArray; \ + \ + /* Reset the index */ \ + Index = 0; \ + \ + /* Loop wait blocks */ \ + do \ + { \ + /* Fill out the wait block */ \ + WaitBlock = &WaitBlockArray[Index]; \ + WaitBlock->Object = Object[Index]; \ + WaitBlock->WaitKey = (USHORT)Index; \ + WaitBlock->WaitType = WaitType; \ + WaitBlock->Thread = Thread; \ + \ + /* Link to next block */ \ + WaitBlock->NextWaitBlock = &WaitBlockArray[Index + 1]; \ + Index++; \ + } while (Index < Count); \ + \ + /* Link the last block */ \ + WaitBlock->NextWaitBlock = WaitBlockArray; \ + \ + /* Set default wait status */ \ + Thread->WaitStatus = STATUS_WAIT_0; \ + \ + /* Check if we have a timer */ \ + if (Timeout) \ + { \ + /* Link to the block */ \ + TimerBlock->NextWaitBlock = WaitBlockArray; \ + \ + /* Setup the timer */ \ + KxSetTimerForThreadWait(Timer, *Timeout, &Hand); \ + \ + /* Save the due time for the caller */ \ + DueTime.QuadPart = Timer->DueTime.QuadPart; \ + \ + /* Initialize the list */ \ + InitializeListHead(&Timer->Header.WaitListHead); \ + } \ + \ + /* Set wait settings */ \ + Thread->Alertable = Alertable; \ + Thread->WaitMode = WaitMode; \ + Thread->WaitReason = WaitReason; \ + \ + /* Check if we can swap the thread's stack */ \ + Thread->WaitListEntry.Flink = NULL; \ + Swappable = KiCheckThreadStackSwap(Thread, WaitMode); \ + \ + /* Set the wait time */ \ + Thread->WaitTime = KeTickCount.LowPart; + +#define KxSingleThreadWait() \ + /* Setup the Wait Block */ \ + Thread->WaitBlockList = WaitBlock; \ + WaitBlock->WaitKey = STATUS_SUCCESS; \ + WaitBlock->Object = Object; \ + WaitBlock->WaitType = WaitAny; \ + \ + /* Clear wait status */ \ + Thread->WaitStatus = STATUS_SUCCESS; \ + \ + /* Check if we have a timer */ \ + if (Timeout) \ + { \ + /* Setup the timer */ \ + KxSetTimerForThreadWait(Timer, *Timeout, &Hand); \ + \ + /* Save the due time for the caller */ \ + DueTime.QuadPart = Timer->DueTime.QuadPart; \ + \ + /* Pointer to timer block */ \ + WaitBlock->NextWaitBlock = TimerBlock; \ + TimerBlock->NextWaitBlock = WaitBlock; \ + \ + /* Link the timer to this Wait Block */ \ + Timer->Header.WaitListHead.Flink = &TimerBlock->WaitListEntry; \ + Timer->Header.WaitListHead.Blink = &TimerBlock->WaitListEntry; \ + } \ + else \ + { \ + /* No timer block, just ourselves */ \ + WaitBlock->NextWaitBlock = WaitBlock; \ + } \ + \ + /* Set wait settings */ \ + Thread->Alertable = Alertable; \ + Thread->WaitMode = WaitMode; \ + Thread->WaitReason = WaitReason; \ + \ + /* Check if we can swap the thread's stack */ \ + Thread->WaitListEntry.Flink = NULL; \ + Swappable = KiCheckThreadStackSwap(Thread, WaitMode); \ + \ + /* Set the wait time */ \ + Thread->WaitTime = KeTickCount.LowPart; + +#define KxQueueThreadWait() \ + /* Setup the Wait Block */ \ + Thread->WaitBlockList = WaitBlock; \ + WaitBlock->WaitKey = STATUS_SUCCESS; \ + WaitBlock->Object = Queue; \ + WaitBlock->WaitType = WaitAny; \ + WaitBlock->Thread = Thread; \ + \ + /* Clear wait status */ \ + Thread->WaitStatus = STATUS_SUCCESS; \ + \ + /* Check if we have a timer */ \ + if (Timeout) \ + { \ + /* Setup the timer */ \ + KxSetTimerForThreadWait(Timer, *Timeout, &Hand); \ + \ + /* Save the due time for the caller */ \ + DueTime.QuadPart = Timer->DueTime.QuadPart; \ + \ + /* Pointer to timer block */ \ + WaitBlock->NextWaitBlock = TimerBlock; \ + TimerBlock->NextWaitBlock = WaitBlock; \ + \ + /* Link the timer to this Wait Block */ \ + Timer->Header.WaitListHead.Flink = &TimerBlock->WaitListEntry; \ + Timer->Header.WaitListHead.Blink = &TimerBlock->WaitListEntry; \ + } \ + else \ + { \ + /* No timer block, just ourselves */ \ + WaitBlock->NextWaitBlock = WaitBlock; \ + } \ + \ + /* Set wait settings */ \ + Thread->Alertable = FALSE; \ + Thread->WaitMode = WaitMode; \ + Thread->WaitReason = WrQueue; \ + \ + /* Check if we can swap the thread's stack */ \ + Thread->WaitListEntry.Flink = NULL; \ + Swappable = KiCheckThreadStackSwap(Thread, WaitMode); \ + \ + /* Set the wait time */ \ + Thread->WaitTime = KeTickCount.LowPart; + +// +// Unwaits a Thread +// +FORCEINLINE +VOID +KxUnwaitThread(IN DISPATCHER_HEADER *Object, + IN KPRIORITY Increment) +{ + PLIST_ENTRY WaitEntry, WaitList; + PKWAIT_BLOCK WaitBlock; + PKTHREAD WaitThread; + ULONG WaitKey; + + /* Loop the Wait Entries */ + WaitList = &Object->WaitListHead; + ASSERT(IsListEmpty(&Object->WaitListHead) == FALSE); + WaitEntry = WaitList->Flink; + do + { + /* Get the current wait block */ + WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry); + + /* Get the waiting thread */ + WaitThread = WaitBlock->Thread; + + /* Check the current Wait Mode */ + if (WaitBlock->WaitType == WaitAny) + { + /* Use the actual wait key */ + WaitKey = WaitBlock->WaitKey; + } + else + { + /* Otherwise, use STATUS_KERNEL_APC */ + WaitKey = STATUS_KERNEL_APC; + } + + /* Unwait the thread */ + KiUnwaitThread(WaitThread, WaitKey, Increment); + + /* Next entry */ + WaitEntry = WaitList->Flink; + } while (WaitEntry != WaitList); +} + +// +// Unwaits a Thread waiting on an event +// +FORCEINLINE +VOID +KxUnwaitThreadForEvent(IN PKEVENT Event, + IN KPRIORITY Increment) +{ + PLIST_ENTRY WaitEntry, WaitList; + PKWAIT_BLOCK WaitBlock; + PKTHREAD WaitThread; + + /* Loop the Wait Entries */ + WaitList = &Event->Header.WaitListHead; + ASSERT(IsListEmpty(&Event->Header.WaitListHead) == FALSE); + WaitEntry = WaitList->Flink; + do + { + /* Get the current wait block */ + WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry); + + /* Get the waiting thread */ + WaitThread = WaitBlock->Thread; + + /* Check the current Wait Mode */ + if (WaitBlock->WaitType == WaitAny) + { + /* Un-signal it */ + Event->Header.SignalState = 0; + + /* Un-signal the event and unwait the thread */ + KiUnwaitThread(WaitThread, WaitBlock->WaitKey, Increment); + break; + } + + /* Unwait the thread with STATUS_KERNEL_APC */ + KiUnwaitThread(WaitThread, STATUS_KERNEL_APC, Increment); + + /* Next entry */ + WaitEntry = WaitList->Flink; + } while (WaitEntry != WaitList); +} + // // This routine queues a thread that is ready on the PRCB's ready lists. // If this thread cannot currently run on this CPU, then the thread is @@ -1468,14 +1538,3 @@ KeGetPreviousMode(VOID) return KeGetCurrentThread()->PreviousMode; } -VOID -FORCEINLINE -KiInsertWaitTimer(IN PKTIMER Timer) -{ - /* Now insert it into the Timer List */ - InsertAscendingList(&KiTimerListHead, - Timer, - KTIMER, - TimerListEntry, - DueTime.QuadPart); -} diff --git a/reactos/ntoskrnl/ke/clock.c b/reactos/ntoskrnl/ke/clock.c index ee739922e6e..5df7bcc1f8e 100644 --- a/reactos/ntoskrnl/ke/clock.c +++ b/reactos/ntoskrnl/ke/clock.c @@ -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) diff --git a/reactos/ntoskrnl/ke/dpc.c b/reactos/ntoskrnl/ke/dpc.c index e18eb0e418d..6e481eef1ca 100644 --- a/reactos/ntoskrnl/ke/dpc.c +++ b/reactos/ntoskrnl/ke/dpc.c @@ -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 */ diff --git a/reactos/ntoskrnl/ke/i386/clock.S b/reactos/ntoskrnl/ke/i386/clock.S index 298d23c61d1..a2c54e93117 100644 --- a/reactos/ntoskrnl/ke/i386/clock.S +++ b/reactos/ntoskrnl/ke/i386/clock.S @@ -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 diff --git a/reactos/ntoskrnl/ke/krnlinit.c b/reactos/ntoskrnl/ke/krnlinit.c index 0b709bc7682..4a3524069f3 100644 --- a/reactos/ntoskrnl/ke/krnlinit.c +++ b/reactos/ntoskrnl/ke/krnlinit.c @@ -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; } + + diff --git a/reactos/ntoskrnl/ke/queue.c b/reactos/ntoskrnl/ke/queue.c index c41141caf20..a2bddee7070 100644 --- a/reactos/ntoskrnl/ke/queue.c +++ b/reactos/ntoskrnl/ke/queue.c @@ -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 { diff --git a/reactos/ntoskrnl/ke/timer.c b/reactos/ntoskrnl/ke/timer.c index 160f869cc05..cd32d9d2875 100644 --- a/reactos/ntoskrnl/ke/timer.c +++ b/reactos/ntoskrnl/ke/timer.c @@ -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 #define NDEBUG -#include +#include -/* 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 */ diff --git a/reactos/ntoskrnl/ke/wait.c b/reactos/ntoskrnl/ke/wait.c index 19ec2498323..dabe787ec23 100644 --- a/reactos/ntoskrnl/ke/wait.c +++ b/reactos/ntoskrnl/ke/wait.c @@ -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 {