From 6fd4c50fd3a771f851947d6e7d1b10a1af9fe2dc Mon Sep 17 00:00:00 2001 From: Alex Ionescu Date: Tue, 9 Aug 2005 08:02:05 +0000 Subject: [PATCH] Fix priority formulas, account for saturation, do proper km_um conversions for out of bounds or saturation priorirites, create an internal priority change function to be called if the lock is already held (however it's not efficient yet since ros dispatches instead of schedules, and so the lock is sometimes released, but i did write a small hack to account for that)... this fixes more wine tests svn path=/trunk/; revision=17234 --- reactos/lib/kernel32/thread/thread.c | 82 +++++--- reactos/ntoskrnl/ke/dpc.c | 3 +- reactos/ntoskrnl/ke/kthread.c | 293 ++++++++++++++++++--------- 3 files changed, 259 insertions(+), 119 deletions(-) diff --git a/reactos/lib/kernel32/thread/thread.c b/reactos/lib/kernel32/thread/thread.c index 888a3f84d36..56e4ede53fc 100644 --- a/reactos/lib/kernel32/thread/thread.c +++ b/reactos/lib/kernel32/thread/thread.c @@ -15,6 +15,9 @@ #define NDEBUG #include "../include/debug.h" +/* FIXME: NDK */ +#define HIGH_PRIORITY 31 + /* FUNCTIONS *****************************************************************/ _SEH_FILTER(BaseThreadExceptionFilter) { @@ -536,51 +539,76 @@ SetThreadAffinityMask(HANDLE hThread, /* * @implemented */ -BOOL STDCALL +BOOL +STDCALL SetThreadPriority(HANDLE hThread, - int nPriority) + int nPriority) { - ULONG Prio = nPriority; - NTSTATUS Status; + ULONG Prio = nPriority; + NTSTATUS Status; - Status = NtSetInformationThread(hThread, - ThreadBasePriority, - &Prio, - sizeof(ULONG)); - - if (!NT_SUCCESS(Status)) + /* Check if values forcing saturation should be used */ + if (Prio == THREAD_PRIORITY_TIME_CRITICAL) { - SetLastErrorByStatus(Status); - return(FALSE); + Prio = (HIGH_PRIORITY + 1) / 2; + } + else if (Prio == THREAD_PRIORITY_IDLE) + { + Prio = -((HIGH_PRIORITY + 1) / 2); } - return(TRUE); -} + /* Set the Base Priority */ + Status = NtSetInformationThread(hThread, + ThreadBasePriority, + &Prio, + sizeof(ULONG)); + if (!NT_SUCCESS(Status)) + { + /* Failure */ + SetLastErrorByStatus(Status); + return FALSE; + } + /* Return */ + return TRUE; +} /* * @implemented */ -int STDCALL +int +STDCALL GetThreadPriority(HANDLE hThread) { - THREAD_BASIC_INFORMATION ThreadBasic; - NTSTATUS Status; + THREAD_BASIC_INFORMATION ThreadBasic; + NTSTATUS Status; - Status = NtQueryInformationThread(hThread, - ThreadBasicInformation, - &ThreadBasic, - sizeof(THREAD_BASIC_INFORMATION), - NULL); - if (!NT_SUCCESS(Status)) + /* Query the Base Priority Increment */ + Status = NtQueryInformationThread(hThread, + ThreadBasicInformation, + &ThreadBasic, + sizeof(THREAD_BASIC_INFORMATION), + NULL); + if (!NT_SUCCESS(Status)) { - SetLastErrorByStatus(Status); - return(THREAD_PRIORITY_ERROR_RETURN); + /* Failure */ + SetLastErrorByStatus(Status); + return THREAD_PRIORITY_ERROR_RETURN; } - return(ThreadBasic.BasePriority); -} + /* Do some conversions for out of boundary values */ + if (ThreadBasic.BasePriority > THREAD_BASE_PRIORITY_MAX) + { + ThreadBasic.BasePriority = THREAD_PRIORITY_TIME_CRITICAL; + } + else if (ThreadBasic.BasePriority < THREAD_BASE_PRIORITY_MIN) + { + ThreadBasic.BasePriority = THREAD_PRIORITY_IDLE; + } + /* Return the final result */ + return ThreadBasic.BasePriority; +} /* * @implemented diff --git a/reactos/ntoskrnl/ke/dpc.c b/reactos/ntoskrnl/ke/dpc.c index f71ad3ae624..e1728a41329 100644 --- a/reactos/ntoskrnl/ke/dpc.c +++ b/reactos/ntoskrnl/ke/dpc.c @@ -465,7 +465,8 @@ KiQuantumEnd(VOID) if (OldPriority != NewPriority) { /* Set new Priority */ - CurrentThread->Priority = NewPriority; + BOOLEAN Dummy; /* <- This is a hack anyways... */ + KiSetPriorityThread(CurrentThread, NewPriority, &Dummy); } else { diff --git a/reactos/ntoskrnl/ke/kthread.c b/reactos/ntoskrnl/ke/kthread.c index 2319661e762..ce2c14d1ee2 100644 --- a/reactos/ntoskrnl/ke/kthread.c +++ b/reactos/ntoskrnl/ke/kthread.c @@ -348,8 +348,9 @@ KiAdjustQuantumThread(IN PKTHREAD Thread) if (Priority != Thread->Priority) { /* - * HACK HACK This isn't nice, but it's the only way with our - * current codebase + * FIXME: This should be a call to KiSetPriorityThread but + * due to the current ""scheduler"" in ROS, it can't be done + * cleanly since it actualyl dispatches threads instead. */ Thread->Priority = Priority; } @@ -1054,37 +1055,191 @@ KeQueryBasePriorityThread(IN PKTHREAD Thread) return BasePriorityIncrement; } -/* - * @implemented - */ -LONG STDCALL -KeSetBasePriorityThread (PKTHREAD Thread, - LONG Increment) +VOID +STDCALL +KiSetPriorityThread(PKTHREAD Thread, + KPRIORITY Priority, + PBOOLEAN Released) +{ + KPRIORITY OldPriority = Thread->Priority; + ULONG Mask; + int i; + PKPCR Pcr; + DPRINT("Changing prio to : %lx\n", Priority); + + /* Check if priority changed */ + if (OldPriority != Priority) + { + /* Set it */ + Thread->Priority = Priority; + + /* Choose action based on thread's state */ + if (Thread->State == Ready) + { + /* Remove it from the current queue */ + KiRemoveFromThreadList(Thread); + + /* Re-insert it at its current priority */ + KiInsertIntoThreadList(Priority, Thread); + + /* Check if the old priority was lower */ + if (KeGetCurrentThread()->Priority < Priority) + { + /* Dispatch it immediately */ + KiDispatchThreadNoLock(Ready); + *Released = TRUE; + return; + } + } + else if (Thread->State == Running) + { + /* Check if the new priority is lower */ + if (Priority < OldPriority) + { + /* Check for threads with a higher priority */ + Mask = ~((1 << (Priority + 1)) - 1); + if (PriorityListMask & Mask) + { + /* Found a thread, is it us? */ + if (Thread == KeGetCurrentThread()) + { + /* Dispatch us */ + KiDispatchThreadNoLock(Ready); + *Released = TRUE; + return; + } + else + { + /* Loop every CPU */ + for (i = 0; i < KeNumberProcessors; i++) + { + /* Get the PCR for this CPU */ + Pcr = (PKPCR)(KPCR_BASE + i * PAGE_SIZE); + + /* Reschedule if the new one is already on a CPU */ + if (Pcr->Prcb->CurrentThread == Thread) + { + KeReleaseDispatcherDatabaseLockFromDpcLevel(); + KiRequestReschedule(i); + *Released = TRUE; + return; + } + } + } + } + } + } + } + + /* Return to caller */ + return; +} + /* * Sets thread's base priority relative to the process' base priority * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h + * + * @implemented */ +LONG +STDCALL +KeSetBasePriorityThread (PKTHREAD Thread, + LONG Increment) { - KPRIORITY Priority; - if (Increment < -2) - { - Increment = -2; - } - else if (Increment > 2) - { - Increment = 2; - } - Priority = ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment; - if (Priority < LOW_PRIORITY) - { - Priority = LOW_PRIORITY; - } - else if (Priority >= MAXIMUM_PRIORITY) - { - Thread->BasePriority = HIGH_PRIORITY; - } - KeSetPriorityThread(Thread, Priority); - return 1; + KIRQL OldIrql; + PKPROCESS Process; + KPRIORITY Priority; + KPRIORITY CurrentBasePriority; + KPRIORITY BasePriority; + BOOLEAN Released = FALSE; + LONG CurrentIncrement; + + /* Lock the Dispatcher Database */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Get the process and calculate current BP and BPI */ + Process = Thread->ApcStatePointer[0]->Process; + CurrentBasePriority = Thread->BasePriority; + CurrentIncrement = CurrentBasePriority - Process->BasePriority; + + /* Change to use the SI if Saturation was used */ + if (Thread->Saturation) CurrentIncrement = (HIGH_PRIORITY + 1) / 2 * + Thread->Saturation; + + /* Now check if saturation is being used for the new value */ + if (abs(Increment) >= ((HIGH_PRIORITY + 1) / 2)) + { + /* Check if we need positive or negative saturation */ + Thread->Saturation = (Increment > 0) ? 1 : -1; + } + + /* Normalize the Base Priority */ + BasePriority = Process->BasePriority + Increment; + if (Process->BasePriority >= LOW_REALTIME_PRIORITY) + { + /* Check if it's too low */ + if (BasePriority < LOW_REALTIME_PRIORITY) + BasePriority = LOW_REALTIME_PRIORITY; + + /* Check if it's too high */ + if (BasePriority > HIGH_PRIORITY) BasePriority = HIGH_PRIORITY; + + /* We are at RTP, so use the raw BP */ + Priority = BasePriority; + } + else + { + /* Check if it's entering RTP */ + if (BasePriority >= LOW_REALTIME_PRIORITY) + BasePriority = LOW_REALTIME_PRIORITY - 1; + + /* Check if it's too low */ + if (BasePriority <= LOW_PRIORITY) + BasePriority = 1; + + /* If Saturation is used, then use the raw BP */ + if (Thread->Saturation) + { + Priority = BasePriority; + } + else + { + /* Calculate the new priority */ + Priority = Thread->Priority + (BasePriority - CurrentBasePriority)- + Thread->PriorityDecrement; + + /* Make sure it won't enter RTP ranges */ + if (Priority >= LOW_REALTIME_PRIORITY) + Priority = LOW_REALTIME_PRIORITY - 1; + } + } + + /* Finally set the new base priority */ + Thread->BasePriority = BasePriority; + + /* Reset the decrements */ + Thread->DecrementCount = 0; + Thread->PriorityDecrement = 0; + + /* If the priority will change, reset quantum and change it for real */ + if (Priority != Thread->Priority) + { + Thread->Quantum = Thread->QuantumReset; + KiSetPriorityThread(Thread, Priority, &Released); + } + + /* Release Lock if needed */ + if (!Released) + { + KeReleaseDispatcherDatabaseLock(OldIrql); + } + else + { + KeLowerIrql(OldIrql); + } + + /* Return the Old Increment */ + return CurrentIncrement; } /* @@ -1096,79 +1251,35 @@ KeSetPriorityThread(PKTHREAD Thread, KPRIORITY Priority) { KPRIORITY OldPriority; + BOOLEAN Released = FALSE; KIRQL OldIrql; - PKTHREAD CurrentThread; - ULONG Mask; - int i; - PKPCR Pcr; - - if (Priority < LOW_PRIORITY || Priority >= MAXIMUM_PRIORITY) { - - KEBUGCHECK(0); - } + /* Lock the Dispatcher Database */ OldIrql = KeAcquireDispatcherDatabaseLock(); + /* Save the old Priority */ OldPriority = Thread->Priority; - if (OldPriority != Priority) { + /* Reset the Quantum and Decrements */ + Thread->Quantum = Thread->QuantumReset; + Thread->DecrementCount = 0; + Thread->PriorityDecrement = 0; - CurrentThread = KeGetCurrentThread(); + /* Set the new Priority */ + KiSetPriorityThread(Thread, Priority, &Released); - if (Thread->State == Ready) { - - KiRemoveFromThreadList(Thread); - Thread->BasePriority = Thread->Priority = (CHAR)Priority; - KiInsertIntoThreadList(Priority, Thread); - - if (CurrentThread->Priority < Priority) { - - KiDispatchThreadNoLock(Ready); - KeLowerIrql(OldIrql); - return (OldPriority); - } - - } else if (Thread->State == Running) { - - Thread->BasePriority = Thread->Priority = (CHAR)Priority; - - if (Priority < OldPriority) { - - /* Check for threads with a higher priority */ - Mask = ~((1 << (Priority + 1)) - 1); - if (PriorityListMask & Mask) { - - if (Thread == CurrentThread) { - - KiDispatchThreadNoLock(Ready); - KeLowerIrql(OldIrql); - return (OldPriority); - - } else { - - for (i = 0; i < KeNumberProcessors; i++) { - - Pcr = (PKPCR)(KPCR_BASE + i * PAGE_SIZE); - - if (Pcr->Prcb->CurrentThread == Thread) { - - KeReleaseDispatcherDatabaseLockFromDpcLevel(); - KiRequestReschedule(i); - KeLowerIrql(OldIrql); - return (OldPriority); - } - } - } - } - } - } else { - - Thread->BasePriority = Thread->Priority = (CHAR)Priority; - } + /* Release Lock if needed */ + if (!Released) + { + KeReleaseDispatcherDatabaseLock(OldIrql); + } + else + { + KeLowerIrql(OldIrql); } - KeReleaseDispatcherDatabaseLock(OldIrql); - return(OldPriority); + /* Return Old Priority */ + return OldPriority; } /*