247 lines
5.5 KiB
C
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;
|
|
}
|