diff --git a/reactos/ntoskrnl/include/internal/ke.h b/reactos/ntoskrnl/include/internal/ke.h index 07c663ba52c..633c79a8f03 100644 --- a/reactos/ntoskrnl/include/internal/ke.h +++ b/reactos/ntoskrnl/include/internal/ke.h @@ -286,6 +286,13 @@ KiInsertTimerTable( IN ULONG Hand ); +VOID +FASTCALL +KiTimerListExpire( + IN PLIST_ENTRY ExpiredListHead, + IN KIRQL OldIrql +); + BOOLEAN FASTCALL KiInsertTreeTimer( diff --git a/reactos/ntoskrnl/include/internal/ke_x.h b/reactos/ntoskrnl/include/internal/ke_x.h index dbd9817e496..30fa9e5820f 100644 --- a/reactos/ntoskrnl/include/internal/ke_x.h +++ b/reactos/ntoskrnl/include/internal/ke_x.h @@ -1099,6 +1099,37 @@ KiCheckAlertability(IN PKTHREAD Thread, return STATUS_WAIT_0; } +// +// Called from KiCompleteTimer, KiInsertTreeTimer, KeSetSystemTime +// to remove timer entries +// See Windows HPI blog for more information. +VOID +FORCEINLINE +KiRemoveEntryTimer(IN PKTIMER Timer) +{ + ULONG Hand; + PKTIMER_TABLE_ENTRY TableEntry; + + /* Remove the timer from the timer list and check if it's empty */ + Hand = Timer->Header.Hand; + if (RemoveEntryList(&Timer->TimerListEntry)) + { + /* Get the respective timer table entry */ + TableEntry = &KiTimerTableListHead[Hand]; + if (&TableEntry->Entry == TableEntry->Entry.Flink) + { + /* Set the entry to an infinite absolute time */ + TableEntry->Time.HighPart = 0xFFFFFFFF; + } + } + + /* Clear the list entries on dbg builds so we can tell the timer is gone */ +#if DBG + Timer->TimerListEntry.Flink = NULL; + Timer->TimerListEntry.Blink = NULL; +#endif +} + // // Called by Wait and Queue code to insert a timer for dispatching. // Also called by KeSetTimerEx to insert a timer from the caller. @@ -1127,6 +1158,57 @@ KxInsertTimer(IN PKTIMER Timer, } } +// +// Called by KeSetTimerEx and KiInsertTreeTimer to calculate Due Time +// See the Windows HPI Blog for more information +// +BOOLEAN +FORCEINLINE +KiComputeDueTime(IN PKTIMER Timer, + IN LARGE_INTEGER DueTime, + OUT PULONG Hand) +{ + LARGE_INTEGER InterruptTime, SystemTime, DifferenceTime; + + /* Convert to relative time if needed */ + Timer->Header.Absolute = FALSE; + if (DueTime.HighPart >= 0) + { + /* 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) + { + /* Cancel everything */ + Timer->Header.SignalState = TRUE; + Timer->Header.Hand = 0; + Timer->DueTime.QuadPart = 0; + *Hand = 0; + return FALSE; + } + + /* 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; + return TRUE; +} + // // Called from Unlink and Queue Insert Code. // Also called by timer code when canceling an inserted timer. diff --git a/reactos/ntoskrnl/ke/clock.c b/reactos/ntoskrnl/ke/clock.c index 938399bb62b..67e89151a41 100644 --- a/reactos/ntoskrnl/ke/clock.c +++ b/reactos/ntoskrnl/ke/clock.c @@ -40,7 +40,6 @@ KeSetSystemTime(IN PLARGE_INTEGER NewTime, PKSPIN_LOCK_QUEUE LockQueue; LIST_ENTRY TempList, TempList2; ULONG Hand, i; - PKTIMER_TABLE_ENTRY TimerEntry; /* Sanity checks */ ASSERT((NewTime->HighPart & 0xF0000000) == 0); @@ -57,10 +56,10 @@ KeSetSystemTime(IN PLARGE_INTEGER NewTime, /* Query the system time now */ KeQuerySystemTime(OldTime); - /* Set the new system time */ + /* Set the new system time (ordering of these operations is critical) */ + SharedUserData->SystemTime.High2Time = NewTime->HighPart; SharedUserData->SystemTime.LowPart = NewTime->LowPart; SharedUserData->SystemTime.High1Time = NewTime->HighPart; - SharedUserData->SystemTime.High2Time = NewTime->HighPart; /* Check if this was for the HAL and set the RTC time */ if (HalTime) ExCmosClockIsSane = HalSetRealTimeClock(&TimeFields); @@ -98,16 +97,7 @@ KeSetSystemTime(IN PLARGE_INTEGER NewTime, if (Timer->Header.Absolute) { /* 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; - } - } + KiRemoveEntryTimer(Timer); /* Insert it into our temporary list */ InsertTailList(&TempList, &Timer->TimerListEntry); @@ -138,16 +128,7 @@ KeSetSystemTime(IN PLARGE_INTEGER NewTime, if (KiInsertTimerTable(Timer, Hand)) { /* 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; - } - } + KiRemoveEntryTimer(Timer); /* Insert it into our temporary list */ InsertTailList(&TempList2, &Timer->TimerListEntry); @@ -157,8 +138,8 @@ KeSetSystemTime(IN PLARGE_INTEGER NewTime, KiReleaseTimerLock(LockQueue); } - /* FIXME: Process expired timers! */ - KiReleaseDispatcherLock(OldIrql); + /* Process expired timers. This releases the dispatcher lock. */ + KiTimerListExpire(&TempList2, OldIrql); /* Revert affinity */ KeRevertToUserAffinityThread(); diff --git a/reactos/ntoskrnl/ke/dpc.c b/reactos/ntoskrnl/ke/dpc.c index 324fa9ebff0..0d4fe0256fc 100644 --- a/reactos/ntoskrnl/ke/dpc.c +++ b/reactos/ntoskrnl/ke/dpc.c @@ -86,7 +86,6 @@ KiTimerExpiration(IN PKDPC Dpc, LONG Limit, Index, i; ULONG Timers, ActiveTimers, DpcCalls; PLIST_ENTRY ListHead, NextEntry; - PKTIMER_TABLE_ENTRY TimerEntry; KIRQL OldIrql; PKTIMER Timer; PKDPC TimerDpc; @@ -147,16 +146,7 @@ KiTimerExpiration(IN PKDPC Dpc, { /* 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; - } - } + KiRemoveEntryTimer(Timer); /* Make it non-inserted, unlock it, and signal it */ Timer->Header.Inserted = FALSE; @@ -192,6 +182,7 @@ KiTimerExpiration(IN PKDPC Dpc, } /* Check if we have a DPC */ +#ifndef CONFIG_SMP if (TimerDpc) { /* Setup the DPC Entry */ @@ -199,7 +190,11 @@ KiTimerExpiration(IN PKDPC Dpc, DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine; DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext; DpcCalls++; + ASSERT(DpcCalls < MAX_TIMER_DPCS); } +#else +#error MP Case: Need to check the DPC target CPU so see if we can piggyback +#endif /* Check if we're done processing */ if (!(ActiveTimers) || !(Timers)) @@ -303,6 +298,108 @@ KiTimerExpiration(IN PKDPC Dpc, } } +VOID +FASTCALL +KiTimerListExpire(IN PLIST_ENTRY ExpiredListHead, + IN KIRQL OldIrql) +{ + ULARGE_INTEGER SystemTime; + LARGE_INTEGER Interval; + LONG i; + ULONG DpcCalls = 0; + PKTIMER Timer; + PKDPC TimerDpc; + ULONG Period; + DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS]; + + /* Query system */ + KeQuerySystemTime((PLARGE_INTEGER)&SystemTime); + + /* Loop expired list */ + while (ExpiredListHead->Flink != ExpiredListHead) + { + /* Get the current timer */ + Timer = CONTAINING_RECORD(ExpiredListHead->Flink, KTIMER, TimerListEntry); + + /* Remove it */ + RemoveEntryList(&Timer->TimerListEntry); + + /* Not inserted */ + Timer->Header.Inserted = FALSE; + + /* Signal it */ + 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 */ +#ifndef CONFIG_SMP + if (TimerDpc) + { + /* Setup the DPC Entry */ + DpcEntry[DpcCalls].Dpc = TimerDpc; + DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine; + DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext; + DpcCalls++; + ASSERT(DpcCalls < MAX_TIMER_DPCS); + } +#else +#error MP Case: Need to check the DPC target CPU so see if we can piggyback +#endif + } + + /* 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 */ + KeLowerIrql(OldIrql); + } + else + { + /* Unlock the dispatcher */ + KiReleaseDispatcherLock(OldIrql); + } +} + VOID NTAPI KiQuantumEnd(VOID) diff --git a/reactos/ntoskrnl/ke/timerobj.c b/reactos/ntoskrnl/ke/timerobj.c index 5f6aeb94aa0..c599b3046b8 100644 --- a/reactos/ntoskrnl/ke/timerobj.c +++ b/reactos/ntoskrnl/ke/timerobj.c @@ -29,71 +29,29 @@ KiInsertTreeTimer(IN PKTIMER Timer, BOOLEAN Inserted = FALSE; ULONG Hand = 0; PKSPIN_LOCK_QUEUE LockQueue; - LONGLONG DueTime; - LARGE_INTEGER InterruptTime, SystemTime, DifferenceTime; - PKTIMER_TABLE_ENTRY TimerEntry; - DPRINT("KiInsertTreeTimer(): Timer %p, Interval: %I64d\n", Timer, Interval.QuadPart); - /* Convert to relative time if needed */ - Timer->Header.Absolute = FALSE; - if (Interval.HighPart >= 0) + /* Setup the timer's due time */ + if (KiComputeDueTime(Timer, Interval, &Hand)) { - /* Get System Time */ - KeQuerySystemTime(&SystemTime); + /* Acquire the lock */ + LockQueue = KiAcquireTimerLock(Hand); - /* Do the conversion */ - DifferenceTime.QuadPart = SystemTime.QuadPart - Interval.QuadPart; - - /* Make sure it hasn't already expired */ - Timer->Header.Absolute = TRUE; - if (DifferenceTime.HighPart >= 0) + /* Insert the timer */ + if (KiInsertTimerTable(Timer, Hand)) { - /* Cancel everything */ - Timer->Header.SignalState = TRUE; - Timer->Header.Hand = 0; - Timer->DueTime.QuadPart = 0; - return FALSE; + /* It was already there, remove it */ + KiRemoveEntryTimer(Timer); + Timer->Header.Inserted = FALSE; } - - /* Set the time as Absolute */ - Interval = DifferenceTime; - } - - /* Get the Interrupt Time */ - InterruptTime.QuadPart = KeQueryInterruptTime(); - - /* Recalculate due time */ - DueTime = InterruptTime.QuadPart - Interval.QuadPart; - Timer->DueTime.QuadPart = DueTime; - - /* 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)) + else { - /* Get the entry and check if it's empty */ - TimerEntry = &KiTimerTableListHead[Hand]; - if (IsListEmpty(&TimerEntry->Entry)) - { - /* Clear the time then */ - TimerEntry->Time.HighPart = 0xFFFFFFFF; - } + /* Otherwise, we're now inserted */ + Inserted = TRUE; } - } - else - { - /* Otherwise, we're now inserted */ - Inserted = TRUE; + + /* Release the lock */ + KiReleaseTimerLock(LockQueue); } /* Release the lock and return insert status */ @@ -110,7 +68,6 @@ KiInsertTimerTable(IN PKTIMER Timer, BOOLEAN Expired = FALSE; PLIST_ENTRY ListHead, NextEntry; PKTIMER CurrentTimer; - DPRINT("KiInsertTimerTable(): Timer %p, Hand: %d\n", Timer, Hand); /* Check if the period is zero */ @@ -160,7 +117,6 @@ KiSignalTimer(IN PKTIMER Timer) PKDPC Dpc = Timer->Dpc; ULONG Period = Timer->Period; LARGE_INTEGER Interval, SystemTime; - DPRINT("KiSignalTimer(): Timer %p\n", Timer); /* Set default values */ @@ -212,22 +168,11 @@ KiCompleteTimer(IN PKTIMER Timer, IN PKSPIN_LOCK_QUEUE LockQueue) { LIST_ENTRY ListHead; - PKTIMER_TABLE_ENTRY TimerEntry; BOOLEAN RequestInterrupt = FALSE; - DPRINT("KiCompleteTimer(): Timer %p, LockQueue: %p\n", Timer, LockQueue); /* 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; - } - } + KiRemoveEntryTimer(Timer); /* Link the timer list to our stack */ ListHead.Flink = &Timer->TimerListEntry; @@ -264,7 +209,6 @@ KeCancelTimer(IN OUT PKTIMER Timer) BOOLEAN Inserted; ASSERT_TIMER(Timer); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); - DPRINT("KeCancelTimer(): Timer %p\n", Timer); /* Lock the Database and Raise IRQL */ @@ -301,7 +245,8 @@ KeInitializeTimerEx(OUT PKTIMER Timer, IN TIMER_TYPE Type) { DPRINT("KeInitializeTimerEx(): Timer %p, Type %s\n", - Timer, (Type == NotificationTimer) ? "NotificationTimer" : "SynchronizationTimer"); + Timer, (Type == NotificationTimer) ? + "NotificationTimer" : "SynchronizationTimer"); /* Initialize the Dispatch Header */ KeInitializeDispatcherHeader(&Timer->Header, @@ -352,13 +297,11 @@ KeSetTimerEx(IN OUT PKTIMER Timer, KIRQL OldIrql; BOOLEAN Inserted; ULONG Hand = 0; - LARGE_INTEGER InterruptTime, SystemTime, DifferenceTime; BOOLEAN RequestInterrupt = FALSE; ASSERT_TIMER(Timer); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); - DPRINT("KeSetTimerEx(): Timer %p, DueTime %I64d, Period %d, Dpc %p\n", - Timer, DueTime.QuadPart, Period, Dpc); + Timer, DueTime.QuadPart, Period, Dpc); /* Lock the Database and Raise IRQL */ OldIrql = KiAcquireDispatcherLock(); @@ -370,59 +313,24 @@ KeSetTimerEx(IN OUT PKTIMER Timer, /* Set Default Timer Data */ Timer->Dpc = Dpc; Timer->Period = Period; - - /* Convert to relative time if needed */ - Timer->Header.Absolute = FALSE; - if (DueTime.HighPart >= 0) + if (!KiComputeDueTime(Timer, DueTime, &Hand)) { - /* 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) - { - /* 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); - - /* Exit the dispatcher and return the old state */ - KiExitDispatcher(OldIrql); - return Inserted; - } - - /* Set the time as Absolute */ - DueTime = DifferenceTime; + /* Signal the timer */ + RequestInterrupt = KiSignalTimer(Timer); + + /* Release the dispatcher lock */ + KiReleaseDispatcherLockFromDpcLevel(); + + /* Check if we need to do an interrupt */ + if (RequestInterrupt) HalRequestSoftwareInterrupt(DISPATCH_LEVEL); } - - /* 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); - + else + { + /* Insert the timer */ + Timer->Header.SignalState = FALSE; + KxInsertTimer(Timer, Hand); + } + /* Exit the dispatcher */ KiExitDispatcher(OldIrql);