450 lines
11 KiB
C
450 lines
11 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1989 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
waitsup.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module contains the support routines necessary to support the
|
||
|
generic kernel wait functions. Functions are provided to test if a
|
||
|
wait can be satisfied, to satisfy a wait, and to unwwait a thread.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
David N. Cutler (davec) 24-Mar-1989
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
Kernel mode only.
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "ki.h"
|
||
|
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
KiUnlinkThread (
|
||
|
IN PRKTHREAD Thread,
|
||
|
IN LONG_PTR WaitStatus
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function unlinks a thread from the appropriate wait queues and sets
|
||
|
the thread's wait completion status.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Thread - Supplies a pointer to a dispatcher object of type thread.
|
||
|
|
||
|
WaitStatus - Supplies the wait completion status.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
PKQUEUE Queue;
|
||
|
PKTIMER Timer;
|
||
|
PRKWAIT_BLOCK WaitBlock;
|
||
|
|
||
|
//
|
||
|
// Set wait completion status, remove wait blocks from object wait
|
||
|
// lists, and remove thread from wait list.
|
||
|
//
|
||
|
|
||
|
Thread->WaitStatus |= WaitStatus;
|
||
|
WaitBlock = Thread->WaitBlockList;
|
||
|
do {
|
||
|
RemoveEntryList(&WaitBlock->WaitListEntry);
|
||
|
WaitBlock = WaitBlock->NextWaitBlock;
|
||
|
} while (WaitBlock != Thread->WaitBlockList);
|
||
|
|
||
|
if (Thread->WaitListEntry.Flink != NULL) {
|
||
|
RemoveEntryList(&Thread->WaitListEntry);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If thread timer is still active, then cancel thread timer.
|
||
|
//
|
||
|
|
||
|
Timer = &Thread->Timer;
|
||
|
if (Timer->Header.Inserted != FALSE) {
|
||
|
KiRemoveTreeTimer(Timer);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the thread is processing a queue entry, then increment the
|
||
|
// count of currently active threads.
|
||
|
//
|
||
|
|
||
|
Queue = Thread->Queue;
|
||
|
if (Queue != NULL) {
|
||
|
Queue->CurrentCount += 1;
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
KiUnwaitThread (
|
||
|
IN PRKTHREAD Thread,
|
||
|
IN LONG_PTR WaitStatus,
|
||
|
IN KPRIORITY Increment,
|
||
|
IN PLIST_ENTRY ThreadList OPTIONAL
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function unwaits a thread, sets the thread's wait completion status,
|
||
|
calculates the thread's new priority, and either readies the thread for
|
||
|
execution or adds the thread to a list of threads to be readied later.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Thread - Supplies a pointer to a dispatcher object of type thread.
|
||
|
|
||
|
WaitStatus - Supplies the wait completion status.
|
||
|
|
||
|
Increment - Supplies the priority increment that is to be applied to
|
||
|
the thread's priority.
|
||
|
|
||
|
ThreadList - Supplies an optional pointer to a listhead.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
KPRIORITY NewPriority;
|
||
|
PKPROCESS Process;
|
||
|
|
||
|
//
|
||
|
// Unlink thread from the appropriate wait queues and set the wait
|
||
|
// completion status.
|
||
|
//
|
||
|
|
||
|
KiUnlinkThread(Thread, WaitStatus);
|
||
|
|
||
|
//
|
||
|
// If the thread runs at a realtime priority level, then reset the
|
||
|
// thread quantum. Otherwise, compute the next thread priority and
|
||
|
// charge the thread for the wait operation.
|
||
|
//
|
||
|
|
||
|
Process = Thread->ApcState.Process;
|
||
|
if (Thread->Priority < LOW_REALTIME_PRIORITY) {
|
||
|
if ((Thread->PriorityDecrement == 0) &&
|
||
|
(Thread->DisableBoost == FALSE)) {
|
||
|
NewPriority = Thread->BasePriority + Increment;
|
||
|
|
||
|
//
|
||
|
// If the specified thread is from a process with a foreground
|
||
|
// memory priority, then add the foreground boost separation.
|
||
|
//
|
||
|
|
||
|
if (((PEPROCESS)Process)->Vm.Flags.MemoryPriority == MEMORY_PRIORITY_FOREGROUND) {
|
||
|
NewPriority += PsPrioritySeperation;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the new thread priority is greater than the current thread
|
||
|
// priority, then boost the thread priority, but not above low
|
||
|
// real time minus one.
|
||
|
//
|
||
|
|
||
|
if (NewPriority > Thread->Priority) {
|
||
|
if (NewPriority >= LOW_REALTIME_PRIORITY) {
|
||
|
NewPriority = LOW_REALTIME_PRIORITY - 1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the new thread priority is greater than the thread base
|
||
|
// priority plus the specified increment (i.e., the foreground
|
||
|
// separation was added), then set the priority decrement to
|
||
|
// remove the separation boost after one quantum.
|
||
|
//
|
||
|
|
||
|
if (NewPriority > (Thread->BasePriority + Increment)) {
|
||
|
Thread->PriorityDecrement =
|
||
|
(SCHAR)(NewPriority - Thread->BasePriority - Increment);
|
||
|
|
||
|
Thread->DecrementCount = ROUND_TRIP_DECREMENT_COUNT;
|
||
|
}
|
||
|
|
||
|
Thread->Priority = (SCHAR)NewPriority;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (Thread->BasePriority >= TIME_CRITICAL_PRIORITY_BOUND) {
|
||
|
Thread->Quantum = Process->ThreadQuantum;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// If the thread is being unwaited to execute a kernel APC,
|
||
|
// then do not charge the thread any quantum. The wait code
|
||
|
// will charge quantum after the kernel APC has executed and
|
||
|
// the wait is actually satisifed.
|
||
|
//
|
||
|
|
||
|
if (WaitStatus != STATUS_KERNEL_APC) {
|
||
|
Thread->Quantum -= WAIT_QUANTUM_DECREMENT;
|
||
|
if (Thread->Quantum <= 0) {
|
||
|
Thread->Quantum = Process->ThreadQuantum;
|
||
|
Thread->Priority -= (Thread->PriorityDecrement + 1);
|
||
|
if (Thread->Priority < Thread->BasePriority) {
|
||
|
Thread->Priority = Thread->BasePriority;
|
||
|
}
|
||
|
|
||
|
Thread->PriorityDecrement = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
Thread->Quantum = Process->ThreadQuantum;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If a thread list is specified, then add the thread to the enb of the
|
||
|
// specified list. Otherwise, ready the thread for execution.
|
||
|
//
|
||
|
|
||
|
if (ARGUMENT_PRESENT(ThreadList)) {
|
||
|
InsertTailList(ThreadList, &Thread->WaitListEntry);
|
||
|
|
||
|
} else {
|
||
|
KiReadyThread(Thread);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
KeBoostCurrentThread(
|
||
|
VOID
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function boosts the priority of the current thread for one quantum,
|
||
|
then reduce the thread priority to the base priority of the thread.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
KIRQL OldIrql;
|
||
|
PKTHREAD Thread;
|
||
|
|
||
|
//
|
||
|
// Get current thread address, raise IRQL to synchronization level, and
|
||
|
// lock the dispatcher database
|
||
|
//
|
||
|
|
||
|
Thread = KeGetCurrentThread();
|
||
|
|
||
|
redoboost:
|
||
|
KiLockDispatcherDatabase(&OldIrql);
|
||
|
|
||
|
//
|
||
|
// If a priority boost is not already active for the current thread
|
||
|
// and the thread priority is less than 14, then boost the thread
|
||
|
// priority to 14 and give the thread a large quantum. Otherwise,
|
||
|
// if a priority boost is active, then decrement the round trip
|
||
|
// count. If the count goes to zero, then release the dispatcher
|
||
|
// database lock, lower the thread priority to the base priority,
|
||
|
// and then attempt to boost the priority again. This will give
|
||
|
// other threads a chance to run. If the count does not reach zero,
|
||
|
// then give the thread another large qunatum.
|
||
|
//
|
||
|
// If the thread priority is above 14, then no boost is applied.
|
||
|
//
|
||
|
|
||
|
if ((Thread->PriorityDecrement == 0) && (Thread->Priority < 14)) {
|
||
|
Thread->PriorityDecrement = 14 - Thread->BasePriority;
|
||
|
Thread->DecrementCount = ROUND_TRIP_DECREMENT_COUNT;
|
||
|
Thread->Priority = 14;
|
||
|
Thread->Quantum = Thread->ApcState.Process->ThreadQuantum * 2;
|
||
|
|
||
|
} else if (Thread->PriorityDecrement != 0) {
|
||
|
Thread->DecrementCount -= 1;
|
||
|
if (Thread->DecrementCount == 0) {
|
||
|
KiUnlockDispatcherDatabase(OldIrql);
|
||
|
KeSetPriorityThread(Thread, Thread->BasePriority);
|
||
|
goto redoboost;
|
||
|
|
||
|
} else {
|
||
|
Thread->Quantum = Thread->ApcState.Process->ThreadQuantum * 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
KiUnlockDispatcherDatabase(OldIrql);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
KiWaitSatisfyAll (
|
||
|
IN PRKWAIT_BLOCK WaitBlock
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function satisfies a wait all and performs any side effects that
|
||
|
are necessary.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
WaitBlock - Supplies a pointer to a wait block.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
PKMUTANT Object;
|
||
|
PRKTHREAD Thread;
|
||
|
PRKWAIT_BLOCK WaitBlock1;
|
||
|
|
||
|
//
|
||
|
// If the wait type was WaitAny, then perform neccessary side effects on
|
||
|
// the object specified by the wait block. Else perform necessary side
|
||
|
// effects on all the objects that were involved in the wait operation.
|
||
|
//
|
||
|
|
||
|
WaitBlock1 = WaitBlock;
|
||
|
Thread = WaitBlock1->Thread;
|
||
|
do {
|
||
|
if (WaitBlock1->WaitKey != (CSHORT)STATUS_TIMEOUT) {
|
||
|
Object = (PKMUTANT)WaitBlock1->Object;
|
||
|
KiWaitSatisfyAny(Object, Thread);
|
||
|
}
|
||
|
|
||
|
WaitBlock1 = WaitBlock1->NextWaitBlock;
|
||
|
} while (WaitBlock1 != WaitBlock);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
KiWaitTest (
|
||
|
IN PVOID Object,
|
||
|
IN KPRIORITY Increment
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function tests if a wait can be satisfied when an object attains
|
||
|
a state of signaled. If a wait can be satisfied, then the subject thread
|
||
|
is unwaited with a completion status that is the WaitKey of the wait
|
||
|
block from the object wait list. As many waits as possible are satisfied.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Object - Supplies a pointer to a dispatcher object.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
PKEVENT Event;
|
||
|
PLIST_ENTRY ListHead;
|
||
|
PRKWAIT_BLOCK NextBlock;
|
||
|
PKMUTANT Mutant;
|
||
|
LIST_ENTRY ReadyList;
|
||
|
PRKTHREAD Thread;
|
||
|
PLIST_ENTRY ThreadEntry;
|
||
|
PRKWAIT_BLOCK WaitBlock;
|
||
|
PLIST_ENTRY WaitEntry;
|
||
|
NTSTATUS WaitStatus;
|
||
|
|
||
|
//
|
||
|
// As long as the signal state of the specified object is Signaled and
|
||
|
// there are waiters in the object wait list, then try to satisfy a wait.
|
||
|
//
|
||
|
|
||
|
Event = (PKEVENT)Object;
|
||
|
ListHead = &Event->Header.WaitListHead;
|
||
|
WaitEntry = ListHead->Flink;
|
||
|
InitializeListHead(&ReadyList);
|
||
|
while ((Event->Header.SignalState > 0) &&
|
||
|
(WaitEntry != ListHead)) {
|
||
|
|
||
|
WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
|
||
|
Thread = WaitBlock->Thread;
|
||
|
WaitStatus = STATUS_KERNEL_APC;
|
||
|
|
||
|
//
|
||
|
// N.B. The below code only satisfies the wait for wait any types.
|
||
|
// Wait all types are satisfied in the wait code itself. This
|
||
|
// is done with a eye to the future when the dispatcher lock is
|
||
|
// split into a lock per waitable object type and a scheduling
|
||
|
// state lock. For now, a kernel APC is simulated for wait all
|
||
|
// types.
|
||
|
//
|
||
|
|
||
|
if (WaitBlock->WaitType == WaitAny) {
|
||
|
WaitStatus = (NTSTATUS)WaitBlock->WaitKey;
|
||
|
KiWaitSatisfyAny((PKMUTANT)Event, Thread);
|
||
|
}
|
||
|
|
||
|
KiUnwaitThread(Thread, WaitStatus, Increment, &ReadyList);
|
||
|
WaitEntry = ListHead->Flink;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Ready any threads which have been made eligible to run. This
|
||
|
// must be done AFTER the event is no longer needed to avoid running
|
||
|
// the thread which owns the event before this routine is finished
|
||
|
// looking at it.
|
||
|
//
|
||
|
|
||
|
while (!IsListEmpty(&ReadyList)) {
|
||
|
ThreadEntry = RemoveHeadList(&ReadyList);
|
||
|
Thread = CONTAINING_RECORD(ThreadEntry, KTHREAD, WaitListEntry);
|
||
|
KiReadyThread(Thread);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|