825 lines
20 KiB
C
825 lines
20 KiB
C
/*++
|
||
|
||
Copyright (c) 1991-1994 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
balmgr.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the NT balance set manager. Normally the kernel
|
||
does not contain "policy" code. However, the balance set manager needs
|
||
to be able to traverse the kernel data structures and, therefore, the
|
||
code has been located as logically part of the kernel.
|
||
|
||
The balance set manager performs the following operations:
|
||
|
||
1. Makes the kernel stack of threads that have been waiting for a
|
||
certain amount of time, nonresident.
|
||
|
||
2. Removes processes from the balance set when memory gets tight
|
||
and brings processes back into the balance set when there is
|
||
more memory available.
|
||
|
||
3. Makes the kernel stack resident for threads whose wait has been
|
||
completed, but whose stack is nonresident.
|
||
|
||
4. Arbitrarily boosts the priority of a selected set of threads
|
||
to prevent priority inversion in variable priority levels.
|
||
|
||
In general, the balance set manager only is active during periods when
|
||
memory is tight.
|
||
|
||
Author:
|
||
|
||
David N. Cutler (davec) 13-Jul-1991
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "ki.h"
|
||
|
||
//
|
||
// Define balance set wait object types.
|
||
//
|
||
|
||
typedef enum _BALANCE_OBJECT {
|
||
TimerExpiration,
|
||
WorkingSetManagerEvent,
|
||
MaximumObject
|
||
} BALANCE_OBJECT;
|
||
|
||
//
|
||
// Define maximum number of thread stacks that can be out swapped in
|
||
// a single time period.
|
||
//
|
||
|
||
#define MAXIMUM_THREAD_STACKS 20
|
||
|
||
//
|
||
// Define periodic wait interval value.
|
||
//
|
||
|
||
#define PERIODIC_INTERVAL (1 * 1000 * 1000 * 10)
|
||
|
||
//
|
||
// Define amount of time a thread can be in the ready state without having
|
||
// is priority boosted (approximately 4 seconds).
|
||
//
|
||
|
||
#define READY_WITHOUT_RUNNING (4 * 75)
|
||
|
||
//
|
||
// Define kernel stack protect time. For small systems the protect time
|
||
// is 3 seconds. For all other systems, the protect time is 7 seconds.
|
||
//
|
||
|
||
#define SMALL_SYSTEM_STACK_PROTECT_TIME (3 * 75)
|
||
#define STACK_PROTECT_TIME (7 * 75)
|
||
#define STACK_SCAN_PERIOD 4
|
||
ULONG KiStackProtectTime;
|
||
|
||
//
|
||
// Define number of threads to scan each period and the priority boost bias.
|
||
//
|
||
|
||
#define THREAD_BOOST_BIAS 2
|
||
#define THREAD_BOOST_PRIORITY (LOW_REALTIME_PRIORITY - THREAD_BOOST_BIAS)
|
||
#define THREAD_SCAN_PRIORITY (THREAD_BOOST_PRIORITY - 1)
|
||
#define THREAD_READY_COUNT 10
|
||
#define THREAD_SCAN_COUNT 16
|
||
|
||
//
|
||
// Define local procedure prototypes.
|
||
//
|
||
|
||
VOID
|
||
KiInSwapKernelStacks (
|
||
IN KIRQL PreviousIrql
|
||
);
|
||
|
||
VOID
|
||
KiInSwapProcesses (
|
||
IN KIRQL PreviousIrql
|
||
);
|
||
|
||
VOID
|
||
KiOutSwapKernelStacks (
|
||
IN KIRQL PreviousIrql
|
||
);
|
||
|
||
VOID
|
||
KiOutSwapProcesses (
|
||
IN KIRQL PreviousIrql
|
||
);
|
||
|
||
VOID
|
||
KiScanReadyQueues (
|
||
VOID
|
||
);
|
||
|
||
//
|
||
// Define thread table index static data.
|
||
//
|
||
|
||
ULONG KiReadyQueueIndex = 1;
|
||
|
||
//
|
||
// Define swap request flag.
|
||
//
|
||
|
||
BOOLEAN KiStackOutSwapRequest = FALSE;
|
||
|
||
VOID
|
||
KeBalanceSetManager (
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the startup code for the balance set manager. The
|
||
balance set manager thread is created during system initialization
|
||
and begins execution in this function.
|
||
|
||
Arguments:
|
||
|
||
Context - Supplies a pointer to an arbitrary data structure (NULL).
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
LARGE_INTEGER DueTime;
|
||
KTIMER PeriodTimer;
|
||
KIRQL OldIrql;
|
||
ULONG StackScanPeriod;
|
||
NTSTATUS Status;
|
||
KWAIT_BLOCK WaitBlockArray[MaximumObject];
|
||
PVOID WaitObjects[MaximumObject];
|
||
|
||
//
|
||
// Raise the thread priority to the lowest realtime level.
|
||
//
|
||
|
||
KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
|
||
|
||
//
|
||
// Initialize the periodic timer, set it to expire one period from
|
||
// now, and set the stack scan period.
|
||
//
|
||
|
||
KeInitializeTimer(&PeriodTimer);
|
||
DueTime.QuadPart = - PERIODIC_INTERVAL;
|
||
KeSetTimer(&PeriodTimer, DueTime, NULL);
|
||
StackScanPeriod = STACK_SCAN_PERIOD;
|
||
|
||
//
|
||
// Initialize the wait objects array.
|
||
//
|
||
|
||
WaitObjects[TimerExpiration] = (PVOID)&PeriodTimer;
|
||
WaitObjects[WorkingSetManagerEvent] = (PVOID)&MmWorkingSetManagerEvent;
|
||
|
||
//
|
||
// Loop forever processing balance set manager events.
|
||
//
|
||
|
||
do {
|
||
|
||
//
|
||
// Wait for a memory management memory low event, a swap event,
|
||
// or the expiration of the period timout rate that the balance
|
||
// set manager runs at.
|
||
//
|
||
|
||
Status = KeWaitForMultipleObjects(MaximumObject,
|
||
&WaitObjects[0],
|
||
WaitAny,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL,
|
||
&WaitBlockArray[0]);
|
||
|
||
//
|
||
// Switch on the wait status.
|
||
//
|
||
|
||
switch (Status) {
|
||
|
||
//
|
||
// Periodic timer expiration.
|
||
//
|
||
|
||
case TimerExpiration:
|
||
|
||
//
|
||
// Attempt to initiate outswaping of kernel stacks.
|
||
//
|
||
|
||
StackScanPeriod -= 1;
|
||
if (StackScanPeriod == 0) {
|
||
StackScanPeriod = STACK_SCAN_PERIOD;
|
||
KiLockDispatcherDatabase(&OldIrql);
|
||
if (KiStackOutSwapRequest == FALSE) {
|
||
KiStackOutSwapRequest = TRUE;
|
||
KiUnlockDispatcherDatabase(OldIrql);
|
||
KeSetEvent(&KiSwapEvent, 0, FALSE);
|
||
|
||
} else {
|
||
KiUnlockDispatcherDatabase(OldIrql);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Adjust the depth of lookaside lists.
|
||
//
|
||
|
||
ExAdjustLookasideDepth();
|
||
|
||
//
|
||
// Scan ready queues and boost thread priorities as appropriate.
|
||
//
|
||
|
||
KiScanReadyQueues();
|
||
|
||
//
|
||
// Execute the virtual memory working set manager.
|
||
//
|
||
|
||
MmWorkingSetManager();
|
||
|
||
//
|
||
// Set the timer to expire at the next periodic interval.
|
||
//
|
||
|
||
KeSetTimer(&PeriodTimer, DueTime, NULL);
|
||
break;
|
||
|
||
//
|
||
// Working set manager event.
|
||
//
|
||
|
||
case WorkingSetManagerEvent:
|
||
|
||
//
|
||
// Call the working set manager to trim working sets.
|
||
//
|
||
|
||
MmWorkingSetManager();
|
||
break;
|
||
|
||
//
|
||
// Illegal return status.
|
||
//
|
||
|
||
default:
|
||
KdPrint(("BALMGR: Illegal wait status, %lx =\n", Status));
|
||
break;
|
||
}
|
||
|
||
} while (TRUE);
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
KeSwapProcessOrStack (
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This thread controls the swapping of processes and kernel stacks. The
|
||
order of evaluation is:
|
||
|
||
Outswap kernel stacks
|
||
Outswap processes
|
||
Inswap processes
|
||
Inswap kernel stacks
|
||
|
||
Arguments:
|
||
|
||
Context - Supplies a pointer to the routine context - not used.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
KIRQL OldIrql;
|
||
NTSTATUS Status;
|
||
|
||
//
|
||
// Raise the thread priority to the lowest realtime level + 7 (i.e.,
|
||
// priority 23).
|
||
//
|
||
|
||
KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY + 7);
|
||
|
||
if (MmQuerySystemSize() == MmSmallSystem) {
|
||
KiStackProtectTime = SMALL_SYSTEM_STACK_PROTECT_TIME;
|
||
} else {
|
||
KiStackProtectTime = STACK_PROTECT_TIME;
|
||
}
|
||
|
||
//
|
||
// Loop for ever processing swap events.
|
||
//
|
||
|
||
do {
|
||
|
||
//
|
||
// Wait for a swap event to occur.
|
||
//
|
||
|
||
Status = KeWaitForSingleObject(&KiSwapEvent,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
|
||
//
|
||
// Raise IRQL to dispatcher level and lock dispatcher database.
|
||
//
|
||
|
||
KiLockDispatcherDatabase(&OldIrql);
|
||
|
||
//
|
||
// Loop until all of the four possible actions cannot be initiated.
|
||
//
|
||
|
||
do {
|
||
|
||
//
|
||
// If a request has been made to out swap kernel stacks, then
|
||
// attempt to outswap kernel stacks. Otherwise, if the process
|
||
// out swap list is not empty, then initiate process outswapping.
|
||
// Otherwise, if the process inswap list is not empty, then start
|
||
// process inswapping. Otherwise, if the kernal stack inswap list
|
||
// is not active, then initiate kernel stack inswapping. Otherwise,
|
||
// no work is available.
|
||
//
|
||
|
||
if (KiStackOutSwapRequest != FALSE) {
|
||
KiStackOutSwapRequest = FALSE;
|
||
KiOutSwapKernelStacks(OldIrql);
|
||
continue;
|
||
|
||
} else if (IsListEmpty(&KiProcessOutSwapListHead) == FALSE) {
|
||
KiOutSwapProcesses(OldIrql);
|
||
continue;
|
||
|
||
} else if (IsListEmpty(&KiProcessInSwapListHead) == FALSE) {
|
||
KiInSwapProcesses(OldIrql);
|
||
continue;
|
||
|
||
} else if (IsListEmpty(&KiStackInSwapListHead) == FALSE) {
|
||
KiInSwapKernelStacks(OldIrql);
|
||
continue;
|
||
|
||
} else {
|
||
break;
|
||
}
|
||
} while (TRUE);
|
||
|
||
//
|
||
// Unlock the dispatcher database and lower IRQL to its previous
|
||
// value.
|
||
//
|
||
|
||
KiUnlockDispatcherDatabase(OldIrql);
|
||
} while (TRUE);
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
KiInSwapKernelStacks (
|
||
IN KIRQL PreviousIrql
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function in swaps the kernel stack for threads whose wait has been
|
||
completed and whose kernel stack is nonresident.
|
||
|
||
N.B. The dispatcher data lock is held on entry to this routine and must
|
||
be help on exit to this routine.
|
||
|
||
Arguments:
|
||
|
||
PreviousIrql - Supplies the previous IRQL.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PLIST_ENTRY NextEntry;
|
||
KIRQL OldIrql;
|
||
PKTHREAD Thread;
|
||
|
||
//
|
||
// Process the stack in swap list and for each thread removed from the
|
||
// list, make its kernel stack resident, and ready it for execution.
|
||
//
|
||
|
||
OldIrql = PreviousIrql;
|
||
NextEntry = KiStackInSwapListHead.Flink;
|
||
while (NextEntry != &KiStackInSwapListHead) {
|
||
Thread = CONTAINING_RECORD(NextEntry, KTHREAD, WaitListEntry);
|
||
RemoveEntryList(NextEntry);
|
||
KiUnlockDispatcherDatabase(OldIrql);
|
||
MmInPageKernelStack(Thread);
|
||
KiLockDispatcherDatabase(&OldIrql);
|
||
Thread->KernelStackResident = TRUE;
|
||
KiReadyThread(Thread);
|
||
NextEntry = KiStackInSwapListHead.Flink;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
KiInSwapProcesses (
|
||
IN KIRQL PreviousIrql
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function in swaps processes.
|
||
|
||
N.B. The dispatcher data lock is held on entry to this routine and must
|
||
be help on exit to this routine.
|
||
|
||
Arguments:
|
||
|
||
PreviousIrql - Supplies the previous IRQL.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PLIST_ENTRY NextEntry;
|
||
KIRQL OldIrql;
|
||
PKPROCESS Process;
|
||
PKTHREAD Thread;
|
||
|
||
//
|
||
// Process the process in swap list and for each process removed from
|
||
// the list, make the process resident, and process its ready list.
|
||
//
|
||
|
||
OldIrql = PreviousIrql;
|
||
NextEntry = KiProcessInSwapListHead.Flink;
|
||
while (NextEntry != &KiProcessInSwapListHead) {
|
||
Process = CONTAINING_RECORD(NextEntry, KPROCESS, SwapListEntry);
|
||
RemoveEntryList(NextEntry);
|
||
KiUnlockDispatcherDatabase(OldIrql);
|
||
MmInSwapProcess(Process);
|
||
KiLockDispatcherDatabase(&OldIrql);
|
||
Process->State = ProcessInMemory;
|
||
NextEntry = Process->ReadyListHead.Flink;
|
||
while (NextEntry != &Process->ReadyListHead) {
|
||
Thread = CONTAINING_RECORD(NextEntry, KTHREAD, WaitListEntry);
|
||
RemoveEntryList(NextEntry);
|
||
Thread->ProcessReadyQueue = FALSE;
|
||
KiReadyThread(Thread);
|
||
NextEntry = Process->ReadyListHead.Flink;
|
||
}
|
||
|
||
NextEntry = KiProcessInSwapListHead.Flink;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
KiOutSwapKernelStacks (
|
||
IN KIRQL PreviousIrql
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function attempts to out swap the kernel stack for threads whose
|
||
wait mode is user and which have been waiting longer than the stack
|
||
protect time.
|
||
|
||
N.B. The dispatcher data lock is held on entry to this routine and must
|
||
be help on exit to this routine.
|
||
|
||
Arguments:
|
||
|
||
PreviousIrql - Supplies the previous IRQL.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG CurrentTick;
|
||
PLIST_ENTRY NextEntry;
|
||
ULONG NumberOfThreads;
|
||
KIRQL OldIrql;
|
||
PKPROCESS Process;
|
||
PKTHREAD Thread;
|
||
PKTHREAD ThreadObjects[MAXIMUM_THREAD_STACKS];
|
||
ULONG WaitTime;
|
||
|
||
//
|
||
// Scan the waiting in list and check if the wait time exceeds the
|
||
// stack protect time. If the protect time is exceeded, then make
|
||
// the kernel stack of the waiting thread nonresident. If the count
|
||
// of the number of stacks that are resident for the process reaches
|
||
// zero, then insert the process in the outswap list and set its state
|
||
// to transition.
|
||
//
|
||
|
||
CurrentTick = KiQueryLowTickCount();
|
||
OldIrql = PreviousIrql;
|
||
NextEntry = KiWaitInListHead.Flink;
|
||
NumberOfThreads = 0;
|
||
while ((NextEntry != &KiWaitInListHead) &&
|
||
(NumberOfThreads < MAXIMUM_THREAD_STACKS)) {
|
||
Thread = CONTAINING_RECORD(NextEntry, KTHREAD, WaitListEntry);
|
||
|
||
ASSERT(Thread->WaitMode == UserMode);
|
||
|
||
NextEntry = NextEntry->Flink;
|
||
WaitTime = CurrentTick - Thread->WaitTime;
|
||
if ((WaitTime >= KiStackProtectTime) &&
|
||
KiIsThreadNumericStateSaved(Thread)) {
|
||
Thread->KernelStackResident = FALSE;
|
||
ThreadObjects[NumberOfThreads] = Thread;
|
||
NumberOfThreads += 1;
|
||
RemoveEntryList(&Thread->WaitListEntry);
|
||
InsertTailList(&KiWaitOutListHead, &Thread->WaitListEntry);
|
||
Process = Thread->ApcState.Process;
|
||
Process->StackCount -= 1;
|
||
if (Process->StackCount == 0) {
|
||
Process->State = ProcessInTransition;
|
||
InsertTailList(&KiProcessOutSwapListHead,
|
||
&Process->SwapListEntry);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Unlock the dispatcher database and lower IRQL to its previous
|
||
// value.
|
||
//
|
||
|
||
KiUnlockDispatcherDatabase(OldIrql);
|
||
|
||
//
|
||
// Out swap the kernels stack for the selected set of threads.
|
||
//
|
||
|
||
while (NumberOfThreads > 0) {
|
||
NumberOfThreads -= 1;
|
||
Thread = ThreadObjects[NumberOfThreads];
|
||
MmOutPageKernelStack(Thread);
|
||
}
|
||
|
||
//
|
||
// Raise IRQL to dispatcher level and lock dispatcher database.
|
||
//
|
||
|
||
KiLockDispatcherDatabase(&OldIrql);
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
KiOutSwapProcesses (
|
||
IN KIRQL PreviousIrql
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function out swaps processes.
|
||
|
||
N.B. The dispatcher data lock is held on entry to this routine and must
|
||
be help on exit to this routine.
|
||
|
||
Arguments:
|
||
|
||
PreviousIrql - Supplies the previous IRQL.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PLIST_ENTRY NextEntry;
|
||
KIRQL OldIrql;
|
||
PKPROCESS Process;
|
||
PKTHREAD Thread;
|
||
|
||
//
|
||
// Process the process out swap list and for each process removed from
|
||
// the list, make the process nonresident, and process its ready list.
|
||
//
|
||
|
||
OldIrql = PreviousIrql;
|
||
NextEntry = KiProcessOutSwapListHead.Flink;
|
||
while (NextEntry != &KiProcessOutSwapListHead) {
|
||
Process = CONTAINING_RECORD(NextEntry, KPROCESS, SwapListEntry);
|
||
RemoveEntryList(NextEntry);
|
||
NextEntry = Process->ReadyListHead.Flink;
|
||
if (NextEntry != &Process->ReadyListHead) {
|
||
Process->State = ProcessInMemory;
|
||
while (NextEntry != &Process->ReadyListHead) {
|
||
Thread = CONTAINING_RECORD(NextEntry, KTHREAD, WaitListEntry);
|
||
RemoveEntryList(NextEntry);
|
||
Thread->ProcessReadyQueue = FALSE;
|
||
KiReadyThread(Thread);
|
||
NextEntry = Process->ReadyListHead.Flink;
|
||
}
|
||
|
||
} else {
|
||
Process->State = ProcessOutOfMemory;
|
||
KiUnlockDispatcherDatabase(OldIrql);
|
||
MmOutSwapProcess(Process);
|
||
KiLockDispatcherDatabase(&OldIrql);
|
||
}
|
||
|
||
NextEntry = KiProcessOutSwapListHead.Flink;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
KiScanReadyQueues (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function scans a section of the ready queues and attempts to
|
||
boost the priority of threads that run at variable priority levels.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG Count;
|
||
ULONG CurrentTick;
|
||
PLIST_ENTRY Entry;
|
||
ULONG Index;
|
||
PLIST_ENTRY ListHead;
|
||
ULONG Number;
|
||
KIRQL OldIrql;
|
||
PKPROCESS Process;
|
||
ULONG Summary;
|
||
PKTHREAD Thread;
|
||
ULONG WaitTime;
|
||
|
||
//
|
||
// Lock the dispatcher database and check if there are any ready threads
|
||
// queued at the scannable priority levels.
|
||
//
|
||
|
||
KiLockDispatcherDatabase(&OldIrql);
|
||
Summary = KiReadySummary & ((1 << THREAD_BOOST_PRIORITY) - 2);
|
||
if (Summary != 0) {
|
||
Count = THREAD_READY_COUNT;
|
||
CurrentTick = KiQueryLowTickCount();
|
||
Index = KiReadyQueueIndex;
|
||
Number = THREAD_SCAN_COUNT;
|
||
do {
|
||
|
||
//
|
||
// If the current ready queue index is beyond the end of the range
|
||
// of priorities that are scanned, then wrap back to the beginning
|
||
// priority.
|
||
//
|
||
|
||
if (Index > THREAD_SCAN_PRIORITY) {
|
||
Index = 1;
|
||
}
|
||
|
||
//
|
||
// If there are any ready threads queued at the current priority
|
||
// level, then attempt to boost the thread priority.
|
||
//
|
||
|
||
if (((Summary >> Index) & 1) != 0) {
|
||
Summary ^= (1 << Index);
|
||
ListHead = &KiDispatcherReadyListHead[Index];
|
||
Entry = ListHead->Flink;
|
||
|
||
ASSERT(Entry != ListHead);
|
||
|
||
do {
|
||
Thread = CONTAINING_RECORD(Entry, KTHREAD, WaitListEntry);
|
||
|
||
//
|
||
// If the thread has been waiting for an extended period
|
||
// and is not currently running with a boost, then boost
|
||
// the priority of the current thread.
|
||
//
|
||
|
||
WaitTime = CurrentTick - Thread->WaitTime;
|
||
if ((WaitTime >= READY_WITHOUT_RUNNING) &&
|
||
(Thread->PriorityDecrement == 0)) {
|
||
|
||
//
|
||
// Remove the thread from the respective ready queue.
|
||
//
|
||
|
||
Entry = Entry->Blink;
|
||
RemoveEntryList(Entry->Flink);
|
||
if (IsListEmpty(ListHead) != FALSE) {
|
||
ClearMember(Index, KiReadySummary);
|
||
}
|
||
|
||
//
|
||
// Compute the priority decrement value, set the new
|
||
// thread priority, set the decrement count, set the
|
||
// thread quantum, and ready the thread for execution.
|
||
//
|
||
|
||
Thread->PriorityDecrement =
|
||
THREAD_BOOST_PRIORITY - Thread->Priority;
|
||
|
||
Thread->DecrementCount = ROUND_TRIP_DECREMENT_COUNT;
|
||
Thread->Priority = THREAD_BOOST_PRIORITY;
|
||
Process = Thread->ApcState.Process;
|
||
Thread->Quantum = Process->ThreadQuantum * 2;
|
||
KiReadyThread(Thread);
|
||
Count -= 1;
|
||
}
|
||
|
||
Entry = Entry->Flink;
|
||
Number -= 1;
|
||
} while ((Entry != ListHead) & (Number != 0) & (Count != 0));
|
||
}
|
||
|
||
Index += 1;
|
||
} while ((Summary != 0) & (Number != 0) & (Count != 0));
|
||
}
|
||
|
||
//
|
||
// Unlock the dispatcher database and save the last read queue index
|
||
// for the next scan.
|
||
//
|
||
|
||
KiUnlockDispatcherDatabase(OldIrql);
|
||
if ((Count != 0) && (Number != 0)) {
|
||
KiReadyQueueIndex = 1;
|
||
|
||
} else {
|
||
KiReadyQueueIndex = Index;
|
||
}
|
||
|
||
return;
|
||
}
|