Windows2003-3790/base/ntos/ke/waitsup.c
2020-09-30 16:53:55 +02:00

247 lines
5.5 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
KiExitDispatcher (
IN KIRQL OldIrql
)
/*++
Routine Description:
This function processes the deferred ready list, possibly switches to
a new thread, and lowers IRQL to its previous value.
Arguments:
OldIrql - Supplies the previous IRQL value.
Return Value:
None.
--*/
{
PKTHREAD CurrentThread;
PKTHREAD NewThread;
BOOLEAN Pending;
PKPRCB Prcb;
ASSERT(KeGetCurrentIrql() == SYNCH_LEVEL);
//
// Process the deferred ready list if the list is not empty.
//
Prcb = KeGetCurrentPrcb();
#if !defined(NT_UP)
if (Prcb->DeferredReadyListHead.Next != NULL) {
KiProcessDeferredReadyList(Prcb);
}
#endif
//
// If the old IRQL is less than dispatch level, then a new thread can
// be dispatcher immediately.
//
if (OldIrql < DISPATCH_LEVEL) {
//
// If there is a new thread selected for execution, then switch
// context to the new thread.
//
if (Prcb->NextThread != NULL) {
KiAcquirePrcbLock(Prcb);
NewThread = Prcb->NextThread;
CurrentThread = Prcb->CurrentThread;
KiSetContextSwapBusy(CurrentThread);
Prcb->NextThread = NULL;
Prcb->CurrentThread = NewThread;
NewThread->State = Running;
KxQueueReadyThread(CurrentThread, Prcb);
CurrentThread->WaitIrql = OldIrql;
Pending = KiSwapContext(CurrentThread, NewThread);
if (Pending != FALSE) {
KeLowerIrql(APC_LEVEL);
KiDeliverApc(KernelMode, NULL, NULL);
ASSERT(OldIrql == 0);
}
}
} else if ((Prcb->NextThread != NULL) &&
(Prcb->DpcRoutineActive == FALSE)) {
KiRequestSoftwareInterrupt(DISPATCH_LEVEL);
}
//
// Lower IRQL to its previous level.
//
KeLowerIrql(OldIrql);
return;
}
VOID
FASTCALL
KiUnwaitThread (
IN PRKTHREAD Thread,
IN LONG_PTR WaitStatus,
IN KPRIORITY Increment
)
/*++
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.
Return Value:
None.
--*/
{
//
// Unlink thread from the appropriate wait queues and set the wait
// completion status.
//
KiUnlinkThread(Thread, WaitStatus);
//
// Set unwait priority adjustment parameters.
//
ASSERT(Increment >= 0);
Thread->AdjustIncrement = (SCHAR)Increment;
Thread->AdjustReason = (UCHAR)AdjustUnwait;
//
// Ready the thread for execution.
//
KiReadyThread(Thread);
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;
PRKTHREAD Thread;
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;
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);
WaitEntry = ListHead->Flink;
}
return;
}