1777 lines
54 KiB
C
1777 lines
54 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
wait.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements the generic kernel wait routines. Functions
|
|||
|
are provided to wait for a single object, wait for multiple objects,
|
|||
|
wait for event pair low, wait for event pair high, release and wait
|
|||
|
for semaphore, and to delay thread execution.
|
|||
|
|
|||
|
N.B. This module is written to be a fast as possible and not as small
|
|||
|
as possible. Therefore some code sequences are duplicated to avoid
|
|||
|
procedure calls. It would also be possible to combine wait for
|
|||
|
single object into wait for multiple objects at the cost of some
|
|||
|
speed. Since wait for single object is the most common case, the
|
|||
|
two routines have been separated.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
David N. Cutler (davec) 23-Mar-89
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ki.h"
|
|||
|
|
|||
|
//
|
|||
|
// Test for alertable condition.
|
|||
|
//
|
|||
|
// If alertable is TRUE and the thread is alerted for a processor
|
|||
|
// mode that is equal to the wait mode, then return immediately
|
|||
|
// with a wait completion status of ALERTED.
|
|||
|
//
|
|||
|
// Else if alertable is TRUE, the wait mode is user, and the user APC
|
|||
|
// queue is not empty, then set user APC pending, and return immediately
|
|||
|
// with a wait completion status of USER_APC.
|
|||
|
//
|
|||
|
// Else if alertable is TRUE and the thread is alerted for kernel
|
|||
|
// mode, then return immediately with a wait completion status of
|
|||
|
// ALERTED.
|
|||
|
//
|
|||
|
// Else if alertable is FALSE and the wait mode is user and there is a
|
|||
|
// user APC pending, then return immediately with a wait completion
|
|||
|
// status of USER_APC.
|
|||
|
//
|
|||
|
|
|||
|
#define TestForAlertPending(Alertable) \
|
|||
|
if (Alertable) { \
|
|||
|
if (Thread->Alerted[WaitMode] != FALSE) { \
|
|||
|
Thread->Alerted[WaitMode] = FALSE; \
|
|||
|
WaitStatus = STATUS_ALERTED; \
|
|||
|
break; \
|
|||
|
} else if ((WaitMode != KernelMode) && \
|
|||
|
(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) == FALSE) { \
|
|||
|
Thread->ApcState.UserApcPending = TRUE; \
|
|||
|
WaitStatus = STATUS_USER_APC; \
|
|||
|
break; \
|
|||
|
} else if (Thread->Alerted[KernelMode] != FALSE) { \
|
|||
|
Thread->Alerted[KernelMode] = FALSE; \
|
|||
|
WaitStatus = STATUS_ALERTED; \
|
|||
|
break; \
|
|||
|
} \
|
|||
|
} else if ((WaitMode != KernelMode) && (Thread->ApcState.UserApcPending)) { \
|
|||
|
WaitStatus = STATUS_USER_APC; \
|
|||
|
break; \
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
KeDelayExecutionThread (
|
|||
|
IN KPROCESSOR_MODE WaitMode,
|
|||
|
IN BOOLEAN Alertable,
|
|||
|
IN PLARGE_INTEGER Interval
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function delays the execution of the current thread for the specified
|
|||
|
interval of time.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
WaitMode - Supplies the processor mode in which the delay is to occur.
|
|||
|
|
|||
|
Alertable - Supplies a boolean value that specifies whether the delay
|
|||
|
is alertable.
|
|||
|
|
|||
|
Interval - Supplies a pointer to the absolute or relative time over which
|
|||
|
the delay is to occur.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The wait completion status. A value of STATUS_SUCCESS is returned if
|
|||
|
the delay occurred. A value of STATUS_ALERTED is returned if the wait
|
|||
|
was aborted to deliver an alert to the current thread. A value of
|
|||
|
STATUS_USER_APC is returned if the wait was aborted to deliver a user
|
|||
|
APC to the current thread.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
LARGE_INTEGER NewTime;
|
|||
|
PLARGE_INTEGER OriginalTime;
|
|||
|
PKPRCB Prcb;
|
|||
|
KPRIORITY Priority;
|
|||
|
PRKQUEUE Queue;
|
|||
|
PRKTHREAD Thread;
|
|||
|
PRKTIMER Timer;
|
|||
|
PKWAIT_BLOCK WaitBlock;
|
|||
|
NTSTATUS WaitStatus;
|
|||
|
|
|||
|
//
|
|||
|
// If the dispatcher database lock is not already held, then set the wait
|
|||
|
// IRQL and lock the dispatcher database. Else set boolean wait variable
|
|||
|
// to FALSE.
|
|||
|
//
|
|||
|
|
|||
|
Thread = KeGetCurrentThread();
|
|||
|
if (Thread->WaitNext) {
|
|||
|
Thread->WaitNext = FALSE;
|
|||
|
|
|||
|
} else {
|
|||
|
KiLockDispatcherDatabase(&Thread->WaitIrql);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Start of delay loop.
|
|||
|
//
|
|||
|
// Note this loop is repeated if a kernel APC is delivered in the middle
|
|||
|
// of the delay or a kernel APC is pending on the first attempt through
|
|||
|
// the loop.
|
|||
|
//
|
|||
|
|
|||
|
OriginalTime = Interval;
|
|||
|
WaitBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// Test to determine if a kernel APC is pending.
|
|||
|
//
|
|||
|
// If a kernel APC is pending and the previous IRQL was less than
|
|||
|
// APC_LEVEL, then a kernel APC was queued by another processor just
|
|||
|
// after IRQL was raised to DISPATCH_LEVEL, but before the dispatcher
|
|||
|
// database was locked.
|
|||
|
//
|
|||
|
// N.B. that this can only happen in a multiprocessor system.
|
|||
|
//
|
|||
|
|
|||
|
if (Thread->ApcState.KernelApcPending && (Thread->WaitIrql < APC_LEVEL)) {
|
|||
|
|
|||
|
//
|
|||
|
// Unlock the dispatcher database and lower IRQL to its previous
|
|||
|
// value. An APC interrupt will immediately occur which will result
|
|||
|
// in the delivery of the kernel APC if possible.
|
|||
|
//
|
|||
|
|
|||
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Test for alert pending.
|
|||
|
//
|
|||
|
|
|||
|
TestForAlertPending(Alertable);
|
|||
|
|
|||
|
//
|
|||
|
// Initialize wait block, insert wait block in timer wait list,
|
|||
|
// insert timer in timer queue, put thread in wait state, select
|
|||
|
// next thread to execute, and context switch to next thread.
|
|||
|
//
|
|||
|
// N.B. The timer wait block is initialized when the respective
|
|||
|
// thread is initialized. Thus the constant fields are not
|
|||
|
// reinitialized. These include the wait object, wait key,
|
|||
|
// wait type, and the wait list entry link pointers.
|
|||
|
//
|
|||
|
|
|||
|
Thread->WaitBlockList = WaitBlock;
|
|||
|
Thread->WaitStatus = (NTSTATUS)0;
|
|||
|
Timer = &Thread->Timer;
|
|||
|
WaitBlock->NextWaitBlock = WaitBlock;
|
|||
|
Timer->Header.WaitListHead.Flink = &WaitBlock->WaitListEntry;
|
|||
|
Timer->Header.WaitListHead.Blink = &WaitBlock->WaitListEntry;
|
|||
|
|
|||
|
//
|
|||
|
// If the timer is inserted in the timer tree, then place the
|
|||
|
// current thread in a wait state. Otherwise, attempt to force
|
|||
|
// the current thread to yield the processor to another thread.
|
|||
|
//
|
|||
|
|
|||
|
if (KiInsertTreeTimer(Timer, *Interval) == FALSE) {
|
|||
|
|
|||
|
//
|
|||
|
// If the thread is not a realtime thread, then drop the
|
|||
|
// thread priority to the base priority.
|
|||
|
//
|
|||
|
|
|||
|
Prcb = KeGetCurrentPrcb();
|
|||
|
Priority = Thread->Priority;
|
|||
|
if (Priority < LOW_REALTIME_PRIORITY) {
|
|||
|
if (Priority != Thread->BasePriority) {
|
|||
|
Thread->PriorityDecrement = 0;
|
|||
|
KiSetPriorityThread(Thread, Thread->BasePriority);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If a new thread has not been selected, the attempt to round
|
|||
|
// robin the thread with other threads at the same priority.
|
|||
|
//
|
|||
|
|
|||
|
if (Prcb->NextThread == NULL) {
|
|||
|
Prcb->NextThread = KiFindReadyThread(Thread->NextProcessor,
|
|||
|
Thread->Priority);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If a new thread has been selected for execution, then
|
|||
|
// switch immediately to the selected thread.
|
|||
|
//
|
|||
|
|
|||
|
if (Prcb->NextThread != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Give the current thread a new qunatum and switch
|
|||
|
// context to selected thread.
|
|||
|
//
|
|||
|
// N.B. Control is returned at the original IRQL.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(KeIsExecutingDpc() == FALSE);
|
|||
|
ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
|
|||
|
|
|||
|
Thread->Preempted = FALSE;
|
|||
|
Thread->Quantum = Thread->ApcState.Process->ThreadQuantum;
|
|||
|
|
|||
|
KiReadyThread(Thread);
|
|||
|
WaitStatus = KiSwapThread();
|
|||
|
goto WaitComplete;
|
|||
|
|
|||
|
} else {
|
|||
|
WaitStatus = (NTSTATUS)STATUS_SUCCESS;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the current thread is processing a queue entry, then attempt
|
|||
|
// to activate another thread that is blocked on the queue object.
|
|||
|
//
|
|||
|
|
|||
|
Queue = Thread->Queue;
|
|||
|
if (Queue != NULL) {
|
|||
|
KiActivateWaiterQueue(Queue);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the thread wait parameters, set the thread dispatcher state
|
|||
|
// to Waiting, and insert the thread in the wait list.
|
|||
|
//
|
|||
|
|
|||
|
Thread->Alertable = Alertable;
|
|||
|
Thread->WaitMode = WaitMode;
|
|||
|
Thread->WaitReason = DelayExecution;
|
|||
|
Thread->WaitTime= KiQueryLowTickCount();
|
|||
|
Thread->State = Waiting;
|
|||
|
KiInsertWaitList(WaitMode, Thread);
|
|||
|
|
|||
|
//
|
|||
|
// Switch context to selected thread.
|
|||
|
//
|
|||
|
// N.B. Control is returned at the original IRQL.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(KeIsExecutingDpc() == FALSE);
|
|||
|
ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
|
|||
|
|
|||
|
WaitStatus = KiSwapThread();
|
|||
|
|
|||
|
//
|
|||
|
// If the thread was not awakened to deliver a kernel mode APC,
|
|||
|
// then return the wait status.
|
|||
|
//
|
|||
|
|
|||
|
WaitComplete:
|
|||
|
if (WaitStatus != STATUS_KERNEL_APC) {
|
|||
|
if (WaitStatus == STATUS_TIMEOUT) {
|
|||
|
WaitStatus = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
return WaitStatus;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Reduce the time remaining before the time delay expires.
|
|||
|
//
|
|||
|
|
|||
|
Interval = KiComputeWaitInterval(Timer, OriginalTime, &NewTime);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Raise IRQL to DISPATCH_LEVEL and lock the dispatcher database.
|
|||
|
//
|
|||
|
|
|||
|
KiLockDispatcherDatabase(&Thread->WaitIrql);
|
|||
|
} while (TRUE);
|
|||
|
|
|||
|
//
|
|||
|
// The thread is alerted or a user APC should be delivered. Unlock the
|
|||
|
// dispatcher database, lower IRQL to its previous value, and return the
|
|||
|
// wait status.
|
|||
|
//
|
|||
|
|
|||
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|||
|
return WaitStatus;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
KeReleaseWaitForSemaphore (
|
|||
|
IN PKSEMAPHORE Server,
|
|||
|
IN PKSEMAPHORE Client,
|
|||
|
IN ULONG WaitReason,
|
|||
|
IN ULONG WaitMode
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function releases a semaphore and waits on another semaphore. The
|
|||
|
wait is performed such that an optimal switch to the waiting thread
|
|||
|
occurs if possible. No timeout is associated with the wait, and thus,
|
|||
|
the issuing thread will wait until the semaphore is signaled or an APC
|
|||
|
is delivered.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Server - Supplies a pointer to a dispatcher object of type semaphore.
|
|||
|
|
|||
|
Client - Supplies a pointer to a dispatcher object of type semaphore.
|
|||
|
|
|||
|
WaitReason - Supplies the reason for the wait.
|
|||
|
|
|||
|
WaitMode - Supplies the processor mode in which the wait is to occur.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The wait completion status. A value of STATUS_SUCCESS is returned if
|
|||
|
the specified object satisfied the wait. A value of STATUS_USER_APC is
|
|||
|
returned if the wait was aborted to deliver a user APC to the current
|
|||
|
thread.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PRKTHREAD NextThread;
|
|||
|
LONG OldState;
|
|||
|
PRKQUEUE Queue;
|
|||
|
PRKTHREAD Thread;
|
|||
|
PKWAIT_BLOCK WaitBlock;
|
|||
|
PLIST_ENTRY WaitEntry;
|
|||
|
|
|||
|
//
|
|||
|
// Raise the IRQL to dispatch level and lock the dispatcher database.
|
|||
|
//
|
|||
|
|
|||
|
Thread = KeGetCurrentThread();
|
|||
|
|
|||
|
ASSERT(Thread->WaitNext == FALSE);
|
|||
|
|
|||
|
KiLockDispatcherDatabase(&Thread->WaitIrql);
|
|||
|
|
|||
|
//
|
|||
|
// If the client semaphore is not in the Signaled state and the server
|
|||
|
// semaphore wait queue is not empty, then attempt a direct dispatch
|
|||
|
// to the target thread.
|
|||
|
//
|
|||
|
|
|||
|
if ((Client->Header.SignalState == 0) &&
|
|||
|
(IsListEmpty(&Server->Header.WaitListHead) == FALSE)) {
|
|||
|
|
|||
|
//
|
|||
|
// Get the address of the first waiting server thread.
|
|||
|
//
|
|||
|
|
|||
|
WaitEntry = Server->Header.WaitListHead.Flink;
|
|||
|
WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
|
|||
|
NextThread = WaitBlock->Thread;
|
|||
|
|
|||
|
//
|
|||
|
// Remove the wait block from the semaphore wait list and remove the
|
|||
|
// target thread from the system wait list.
|
|||
|
//
|
|||
|
|
|||
|
RemoveEntryList(&WaitBlock->WaitListEntry);
|
|||
|
RemoveEntryList(&NextThread->WaitListEntry);
|
|||
|
|
|||
|
//
|
|||
|
// If the next thread is processing a queue entry, then increment
|
|||
|
// the current number of threads.
|
|||
|
//
|
|||
|
|
|||
|
Queue = NextThread->Queue;
|
|||
|
if (Queue != NULL) {
|
|||
|
Queue->CurrentCount += 1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Attempt to switch directly to the target thread.
|
|||
|
//
|
|||
|
|
|||
|
return KiSwitchToThread(NextThread, WaitReason, WaitMode, Client);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// If the server sempahore is at the maximum limit, then unlock the
|
|||
|
// dispatcher database and raise an exception.
|
|||
|
//
|
|||
|
|
|||
|
OldState = Server->Header.SignalState;
|
|||
|
if (OldState == Server->Limit) {
|
|||
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|||
|
ExRaiseStatus(STATUS_SEMAPHORE_LIMIT_EXCEEDED);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Signal the server semaphore and test to determine if any wait can be
|
|||
|
// satisfied.
|
|||
|
//
|
|||
|
|
|||
|
Server->Header.SignalState += 1;
|
|||
|
if ((OldState == 0) && (IsListEmpty(&Server->Header.WaitListHead) == FALSE)) {
|
|||
|
KiWaitTest(Server, 1);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Continue the semaphore wait and return the wait completion status.
|
|||
|
//
|
|||
|
// N.B. The wait continuation routine is called with the dispatcher
|
|||
|
// database locked.
|
|||
|
//
|
|||
|
|
|||
|
return KiContinueClientWait(Client, WaitReason, WaitMode);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
KeWaitForMultipleObjects (
|
|||
|
IN ULONG Count,
|
|||
|
IN PVOID Object[],
|
|||
|
IN WAIT_TYPE WaitType,
|
|||
|
IN KWAIT_REASON WaitReason,
|
|||
|
IN KPROCESSOR_MODE WaitMode,
|
|||
|
IN BOOLEAN Alertable,
|
|||
|
IN PLARGE_INTEGER Timeout OPTIONAL,
|
|||
|
IN PKWAIT_BLOCK WaitBlockArray OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function waits until the specified objects attain a state of
|
|||
|
Signaled. The wait can be specified to wait until all of the objects
|
|||
|
attain a state of Signaled or until one of the objects attains a state
|
|||
|
of Signaled. An optional timeout can also be specified. If a timeout
|
|||
|
is not specified, then the wait will not be satisfied until the objects
|
|||
|
attain a state of Signaled. If a timeout is specified, and the objects
|
|||
|
have not attained a state of Signaled when the timeout expires, then
|
|||
|
the wait is automatically satisfied. If an explicit timeout value of
|
|||
|
zero is specified, then no wait will occur if the wait cannot be satisfied
|
|||
|
immediately. The wait can also be specified as alertable.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Count - Supplies a count of the number of objects that are to be waited
|
|||
|
on.
|
|||
|
|
|||
|
Object[] - Supplies an array of pointers to dispatcher objects.
|
|||
|
|
|||
|
WaitType - Supplies the type of wait to perform (WaitAll, WaitAny).
|
|||
|
|
|||
|
WaitReason - Supplies the reason for the wait.
|
|||
|
|
|||
|
WaitMode - Supplies the processor mode in which the wait is to occur.
|
|||
|
|
|||
|
Alertable - Supplies a boolean value that specifies whether the wait is
|
|||
|
alertable.
|
|||
|
|
|||
|
Timeout - Supplies a pointer to an optional absolute of relative time over
|
|||
|
which the wait is to occur.
|
|||
|
|
|||
|
WaitBlockArray - Supplies an optional pointer to an array of wait blocks
|
|||
|
that are to used to describe the wait operation.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The wait completion status. A value of STATUS_TIMEOUT is returned if a
|
|||
|
timeout occurred. The index of the object (zero based) in the object
|
|||
|
pointer array is returned if an object satisfied the wait. A value of
|
|||
|
STATUS_ALERTED is returned if the wait was aborted to deliver an alert
|
|||
|
to the current thread. A value of STATUS_USER_APC is returned if the
|
|||
|
wait was aborted to deliver a user APC to the current thread.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
ULONG Index;
|
|||
|
LARGE_INTEGER NewTime;
|
|||
|
PRKTHREAD NextThread;
|
|||
|
PKMUTANT Objectx;
|
|||
|
PLARGE_INTEGER OriginalTime;
|
|||
|
PRKQUEUE Queue;
|
|||
|
PRKTHREAD Thread;
|
|||
|
PRKTIMER Timer;
|
|||
|
PRKWAIT_BLOCK WaitBlock;
|
|||
|
BOOLEAN WaitSatisfied;
|
|||
|
NTSTATUS WaitStatus;
|
|||
|
PKWAIT_BLOCK WaitTimer;
|
|||
|
|
|||
|
//
|
|||
|
// If the dispatcher database lock is not already held, then set the wait
|
|||
|
// IRQL and lock the dispatcher database. Else set boolean wait variable
|
|||
|
// to FALSE.
|
|||
|
//
|
|||
|
|
|||
|
Thread = KeGetCurrentThread();
|
|||
|
if (Thread->WaitNext) {
|
|||
|
Thread->WaitNext = FALSE;
|
|||
|
|
|||
|
} else {
|
|||
|
KiLockDispatcherDatabase(&Thread->WaitIrql);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If a wait block array has been specified, then the maximum number of
|
|||
|
// objects that can be waited on is specified by MAXIMUM_WAIT_OBJECTS.
|
|||
|
// Otherwise the builtin wait blocks in the thread object are used and
|
|||
|
// the maximum number of objects that can be waited on is specified by
|
|||
|
// THREAD_WAIT_OBJECTS. If the specified number of objects is not within
|
|||
|
// limits, then bug check.
|
|||
|
//
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(WaitBlockArray)) {
|
|||
|
if (Count > MAXIMUM_WAIT_OBJECTS) {
|
|||
|
KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
if (Count > THREAD_WAIT_OBJECTS) {
|
|||
|
KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
|
|||
|
}
|
|||
|
|
|||
|
WaitBlockArray = &Thread->WaitBlock[0];
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Start of wait loop.
|
|||
|
//
|
|||
|
// Note this loop is repeated if a kernel APC is delivered in the middle
|
|||
|
// of the wait or a kernel APC is pending on the first attempt through
|
|||
|
// the loop.
|
|||
|
//
|
|||
|
|
|||
|
OriginalTime = Timeout;
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// Set address of wait block list in thread object.
|
|||
|
//
|
|||
|
|
|||
|
Thread->WaitBlockList = WaitBlockArray;
|
|||
|
|
|||
|
//
|
|||
|
// Test to determine if a kernel APC is pending.
|
|||
|
//
|
|||
|
// If a kernel APC is pending and the previous IRQL was less than
|
|||
|
// APC_LEVEL, then a kernel APC was queued by another processor just
|
|||
|
// after IRQL was raised to DISPATCH_LEVEL, but before the dispatcher
|
|||
|
// database was locked.
|
|||
|
//
|
|||
|
// N.B. that this can only happen in a multiprocessor system.
|
|||
|
//
|
|||
|
|
|||
|
if (Thread->ApcState.KernelApcPending && (Thread->WaitIrql < APC_LEVEL)) {
|
|||
|
|
|||
|
//
|
|||
|
// Unlock the dispatcher database and lower IRQL to its previous
|
|||
|
// value. An APC interrupt will immediately occur which will result
|
|||
|
// in the delivery of the kernel APC if possible.
|
|||
|
//
|
|||
|
|
|||
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Construct wait blocks and check to determine if the wait is
|
|||
|
// already satisfied. If the wait is satisfied, then perform
|
|||
|
// wait completion and return. Else put current thread in a wait
|
|||
|
// state if an explicit timeout value of zero is not specified.
|
|||
|
//
|
|||
|
|
|||
|
Thread->WaitStatus = (NTSTATUS)0;
|
|||
|
WaitSatisfied = TRUE;
|
|||
|
for (Index = 0; Index < Count; Index += 1) {
|
|||
|
|
|||
|
//
|
|||
|
// Test if wait can be satisfied immediately.
|
|||
|
//
|
|||
|
|
|||
|
Objectx = (PKMUTANT)Object[Index];
|
|||
|
|
|||
|
ASSERT(Objectx->Header.Type != QueueObject);
|
|||
|
|
|||
|
if (WaitType == WaitAny) {
|
|||
|
|
|||
|
//
|
|||
|
// If the object is a mutant object and the mutant object
|
|||
|
// has been recursively acquired MINLONG times, then raise
|
|||
|
// an exception. Otherwise if the signal state of the mutant
|
|||
|
// object is greater than zero, or the current thread is
|
|||
|
// the owner of the mutant object, then satisfy the wait.
|
|||
|
//
|
|||
|
|
|||
|
if (Objectx->Header.Type == MutantObject) {
|
|||
|
if ((Objectx->Header.SignalState > 0) ||
|
|||
|
(Thread == Objectx->OwnerThread)) {
|
|||
|
if (Objectx->Header.SignalState != MINLONG) {
|
|||
|
KiWaitSatisfyMutant(Objectx, Thread);
|
|||
|
WaitStatus = (NTSTATUS)(Index) | Thread->WaitStatus;
|
|||
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|||
|
return WaitStatus;
|
|||
|
|
|||
|
} else {
|
|||
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|||
|
ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the signal state is greater than zero, then satisfy
|
|||
|
// the wait.
|
|||
|
//
|
|||
|
|
|||
|
} else if (Objectx->Header.SignalState > 0) {
|
|||
|
KiWaitSatisfyOther(Objectx);
|
|||
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|||
|
return (NTSTATUS)(Index);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// If the object is a mutant object and the mutant object
|
|||
|
// has been recursively acquired MAXLONG times, then raise
|
|||
|
// an exception. Otherwise if the signal state of the mutant
|
|||
|
// object is less than or equal to zero and the current
|
|||
|
// thread is not the owner of the mutant object, then the
|
|||
|
// wait cannot be satisfied.
|
|||
|
//
|
|||
|
|
|||
|
if (Objectx->Header.Type == MutantObject) {
|
|||
|
if ((Thread == Objectx->OwnerThread) &&
|
|||
|
(Objectx->Header.SignalState == MINLONG)) {
|
|||
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|||
|
ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
|
|||
|
|
|||
|
} else if ((Objectx->Header.SignalState <= 0) &&
|
|||
|
(Thread != Objectx->OwnerThread)) {
|
|||
|
WaitSatisfied = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the signal state is less than or equal to zero, then
|
|||
|
// the wait cannot be satisfied.
|
|||
|
//
|
|||
|
|
|||
|
} else if (Objectx->Header.SignalState <= 0) {
|
|||
|
WaitSatisfied = FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Construct wait block for the current object.
|
|||
|
//
|
|||
|
|
|||
|
WaitBlock = &WaitBlockArray[Index];
|
|||
|
WaitBlock->Object = (PVOID)Objectx;
|
|||
|
WaitBlock->WaitKey = (CSHORT)(Index);
|
|||
|
WaitBlock->WaitType = WaitType;
|
|||
|
WaitBlock->Thread = Thread;
|
|||
|
WaitBlock->NextWaitBlock = &WaitBlockArray[Index + 1];
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the wait type is wait all, then check to determine if the
|
|||
|
// wait can be satisfied immediately.
|
|||
|
//
|
|||
|
|
|||
|
if ((WaitType == WaitAll) && (WaitSatisfied)) {
|
|||
|
WaitBlock->NextWaitBlock = &WaitBlockArray[0];
|
|||
|
KiWaitSatisfyAll(WaitBlock);
|
|||
|
WaitStatus = Thread->WaitStatus;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Test for alert pending.
|
|||
|
//
|
|||
|
|
|||
|
TestForAlertPending(Alertable);
|
|||
|
|
|||
|
//
|
|||
|
// The wait cannot be satisifed immediately. Check to determine if
|
|||
|
// a timeout value is specified.
|
|||
|
//
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(Timeout)) {
|
|||
|
|
|||
|
//
|
|||
|
// If the timeout value is zero, then return immediately without
|
|||
|
// waiting.
|
|||
|
//
|
|||
|
|
|||
|
if (!(Timeout->LowPart | Timeout->HighPart)) {
|
|||
|
WaitStatus = (NTSTATUS)(STATUS_TIMEOUT);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize a wait block for the thread specific timer,
|
|||
|
// initialize timer wait list head, insert the timer in the
|
|||
|
// timer tree, and increment the number of wait objects.
|
|||
|
//
|
|||
|
// N.B. The timer wait block is initialized when the respective
|
|||
|
// thread is initialized. Thus the constant fields are not
|
|||
|
// reinitialized. These include the wait object, wait key,
|
|||
|
// wait type, and the wait list entry link pointers.
|
|||
|
//
|
|||
|
|
|||
|
WaitTimer = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
|
|||
|
WaitBlock->NextWaitBlock = WaitTimer;
|
|||
|
WaitBlock = WaitTimer;
|
|||
|
Timer = &Thread->Timer;
|
|||
|
InitializeListHead(&Timer->Header.WaitListHead);
|
|||
|
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
|
|||
|
WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Close up the circular list of wait control blocks.
|
|||
|
//
|
|||
|
|
|||
|
WaitBlock->NextWaitBlock = &WaitBlockArray[0];
|
|||
|
|
|||
|
//
|
|||
|
// Insert wait blocks in object wait lists.
|
|||
|
//
|
|||
|
|
|||
|
WaitBlock = &WaitBlockArray[0];
|
|||
|
do {
|
|||
|
Objectx = (PKMUTANT)WaitBlock->Object;
|
|||
|
InsertTailList(&Objectx->Header.WaitListHead, &WaitBlock->WaitListEntry);
|
|||
|
WaitBlock = WaitBlock->NextWaitBlock;
|
|||
|
} while (WaitBlock != &WaitBlockArray[0]);
|
|||
|
|
|||
|
//
|
|||
|
// If the current thread is processing a queue entry, then attempt
|
|||
|
// to activate another thread that is blocked on the queue object.
|
|||
|
//
|
|||
|
|
|||
|
Queue = Thread->Queue;
|
|||
|
if (Queue != NULL) {
|
|||
|
KiActivateWaiterQueue(Queue);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the thread wait parameters, set the thread dispatcher state
|
|||
|
// to Waiting, and insert the thread in the wait list.
|
|||
|
//
|
|||
|
|
|||
|
Thread->Alertable = Alertable;
|
|||
|
Thread->WaitMode = WaitMode;
|
|||
|
Thread->WaitReason = WaitReason;
|
|||
|
Thread->WaitTime= KiQueryLowTickCount();
|
|||
|
Thread->State = Waiting;
|
|||
|
KiInsertWaitList(WaitMode, Thread);
|
|||
|
|
|||
|
//
|
|||
|
// Switch context to selected thread.
|
|||
|
//
|
|||
|
// Control is returned at the original IRQL.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(KeIsExecutingDpc() == FALSE);
|
|||
|
ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
|
|||
|
|
|||
|
WaitStatus = KiSwapThread();
|
|||
|
|
|||
|
//
|
|||
|
// If the thread was not awakened to deliver a kernel mode APC,
|
|||
|
// then the wait status.
|
|||
|
//
|
|||
|
|
|||
|
if (WaitStatus != STATUS_KERNEL_APC) {
|
|||
|
return WaitStatus;
|
|||
|
}
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(Timeout)) {
|
|||
|
|
|||
|
//
|
|||
|
// Reduce the amount of time remaining before timeout occurs.
|
|||
|
//
|
|||
|
|
|||
|
Timeout = KiComputeWaitInterval(Timer, OriginalTime, &NewTime);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Raise IRQL to DISPATCH_LEVEL and lock the dispatcher database.
|
|||
|
//
|
|||
|
|
|||
|
KiLockDispatcherDatabase(&Thread->WaitIrql);
|
|||
|
} while (TRUE);
|
|||
|
|
|||
|
//
|
|||
|
// The thread is alerted, a user APC should be delivered, or the wait is
|
|||
|
// satisfied. Unlock dispatcher database, lower IRQL to its previous value,
|
|||
|
// and return the wait status.
|
|||
|
//
|
|||
|
|
|||
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|||
|
return WaitStatus;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
KeWaitForSingleObject (
|
|||
|
IN PVOID Object,
|
|||
|
IN KWAIT_REASON WaitReason,
|
|||
|
IN KPROCESSOR_MODE WaitMode,
|
|||
|
IN BOOLEAN Alertable,
|
|||
|
IN PLARGE_INTEGER Timeout OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function waits until the specified object attains a state of
|
|||
|
Signaled. An optional timeout can also be specified. If a timeout
|
|||
|
is not specified, then the wait will not be satisfied until the object
|
|||
|
attains a state of Signaled. If a timeout is specified, and the object
|
|||
|
has not attained a state of Signaled when the timeout expires, then
|
|||
|
the wait is automatically satisfied. If an explicit timeout value of
|
|||
|
zero is specified, then no wait will occur if the wait cannot be satisfied
|
|||
|
immediately. The wait can also be specified as alertable.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Object - Supplies a pointer to a dispatcher object.
|
|||
|
|
|||
|
WaitReason - Supplies the reason for the wait.
|
|||
|
|
|||
|
WaitMode - Supplies the processor mode in which the wait is to occur.
|
|||
|
|
|||
|
Alertable - Supplies a boolean value that specifies whether the wait is
|
|||
|
alertable.
|
|||
|
|
|||
|
Timeout - Supplies a pointer to an optional absolute of relative time over
|
|||
|
which the wait is to occur.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The wait completion status. A value of STATUS_TIMEOUT is returned if a
|
|||
|
timeout occurred. A value of STATUS_SUCCESS is returned if the specified
|
|||
|
object satisfied the wait. A value of STATUS_ALERTED is returned if the
|
|||
|
wait was aborted to deliver an alert to the current thread. A value of
|
|||
|
STATUS_USER_APC is returned if the wait was aborted to deliver a user
|
|||
|
APC to the current thread.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
LARGE_INTEGER NewTime;
|
|||
|
PRKTHREAD NextThread;
|
|||
|
PKMUTANT Objectx;
|
|||
|
PLARGE_INTEGER OriginalTime;
|
|||
|
PRKQUEUE Queue;
|
|||
|
PRKTHREAD Thread;
|
|||
|
PRKTIMER Timer;
|
|||
|
PKWAIT_BLOCK WaitBlock;
|
|||
|
NTSTATUS WaitStatus;
|
|||
|
PKWAIT_BLOCK WaitTimer;
|
|||
|
|
|||
|
//
|
|||
|
// Collect call data.
|
|||
|
//
|
|||
|
|
|||
|
#if defined(_COLLECT_WAIT_SINGLE_CALLDATA_)
|
|||
|
|
|||
|
RECORD_CALL_DATA(&KiWaitSingleCallData);
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// If the dispatcher database lock is not already held, then set the wait
|
|||
|
// IRQL and lock the dispatcher database. Else set boolean wait variable
|
|||
|
// to FALSE.
|
|||
|
//
|
|||
|
|
|||
|
Thread = KeGetCurrentThread();
|
|||
|
if (Thread->WaitNext) {
|
|||
|
Thread->WaitNext = FALSE;
|
|||
|
|
|||
|
} else {
|
|||
|
KiLockDispatcherDatabase(&Thread->WaitIrql);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Start of wait loop.
|
|||
|
//
|
|||
|
// Note this loop is repeated if a kernel APC is delivered in the middle
|
|||
|
// of the wait or a kernel APC is pending on the first attempt through
|
|||
|
// the loop.
|
|||
|
//
|
|||
|
|
|||
|
OriginalTime = Timeout;
|
|||
|
WaitBlock = &Thread->WaitBlock[0];
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// Test to determine if a kernel APC is pending.
|
|||
|
//
|
|||
|
// If a kernel APC is pending and the previous IRQL was less than
|
|||
|
// APC_LEVEL, then a kernel APC was queued by another processor just
|
|||
|
// after IRQL was raised to DISPATCH_LEVEL, but before the dispatcher
|
|||
|
// database was locked.
|
|||
|
//
|
|||
|
// N.B. that this can only happen in a multiprocessor system.
|
|||
|
//
|
|||
|
|
|||
|
if (Thread->ApcState.KernelApcPending && (Thread->WaitIrql < APC_LEVEL)) {
|
|||
|
|
|||
|
//
|
|||
|
// Unlock the dispatcher database and lower IRQL to its previous
|
|||
|
// value. An APC interrupt will immediately occur which will result
|
|||
|
// in the delivery of the kernel APC if possible.
|
|||
|
//
|
|||
|
|
|||
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Test if the wait can be immediately satisfied.
|
|||
|
//
|
|||
|
|
|||
|
Objectx = (PKMUTANT)Object;
|
|||
|
Thread->WaitStatus = (NTSTATUS)0;
|
|||
|
|
|||
|
ASSERT(Objectx->Header.Type != QueueObject);
|
|||
|
|
|||
|
//
|
|||
|
// If the object is a mutant object and the mutant object has been
|
|||
|
// recursively acquired MINLONG times, then raise an exception.
|
|||
|
// Otherwise if the signal state of the mutant object is greater
|
|||
|
// than zero, or the current thread is the owner of the mutant
|
|||
|
// object, then satisfy the wait.
|
|||
|
//
|
|||
|
|
|||
|
if (Objectx->Header.Type == MutantObject) {
|
|||
|
if ((Objectx->Header.SignalState > 0) ||
|
|||
|
(Thread == Objectx->OwnerThread)) {
|
|||
|
if (Objectx->Header.SignalState != MINLONG) {
|
|||
|
KiWaitSatisfyMutant(Objectx, Thread);
|
|||
|
WaitStatus = (NTSTATUS)(0) | Thread->WaitStatus;
|
|||
|
break;
|
|||
|
|
|||
|
} else {
|
|||
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|||
|
ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the signal state is greater than zero, then satisfy the wait.
|
|||
|
//
|
|||
|
|
|||
|
} else if (Objectx->Header.SignalState > 0) {
|
|||
|
KiWaitSatisfyOther(Objectx);
|
|||
|
WaitStatus = (NTSTATUS)(0);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Construct a wait block for the object.
|
|||
|
//
|
|||
|
|
|||
|
Thread->WaitBlockList = WaitBlock;
|
|||
|
WaitBlock->Object = Object;
|
|||
|
WaitBlock->WaitKey = (CSHORT)(STATUS_SUCCESS);
|
|||
|
WaitBlock->WaitType = WaitAny;
|
|||
|
|
|||
|
//
|
|||
|
// Test for alert pending.
|
|||
|
//
|
|||
|
|
|||
|
TestForAlertPending(Alertable);
|
|||
|
|
|||
|
//
|
|||
|
// The wait cannot be satisifed immediately. Check to determine if
|
|||
|
// a timeout value is specified.
|
|||
|
//
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(Timeout)) {
|
|||
|
|
|||
|
//
|
|||
|
// If the timeout value is zero, then return immediately without
|
|||
|
// waiting.
|
|||
|
//
|
|||
|
|
|||
|
if (!(Timeout->LowPart | Timeout->HighPart)) {
|
|||
|
WaitStatus = (NTSTATUS)(STATUS_TIMEOUT);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize a wait block for the thread specific timer, insert
|
|||
|
// wait block in timer wait list, insert the timer in the timer
|
|||
|
// tree.
|
|||
|
//
|
|||
|
// N.B. The timer wait block is initialized when the respective
|
|||
|
// thread is initialized. Thus the constant fields are not
|
|||
|
// reinitialized. These include the wait object, wait key,
|
|||
|
// wait type, and the wait list entry link pointers.
|
|||
|
//
|
|||
|
|
|||
|
Timer = &Thread->Timer;
|
|||
|
WaitTimer = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
|
|||
|
WaitBlock->NextWaitBlock = WaitTimer;
|
|||
|
Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry;
|
|||
|
Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry;
|
|||
|
WaitTimer->NextWaitBlock = WaitBlock;
|
|||
|
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
|
|||
|
WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
WaitBlock->NextWaitBlock = WaitBlock;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Insert wait block in object wait list.
|
|||
|
//
|
|||
|
|
|||
|
InsertTailList(&Objectx->Header.WaitListHead, &WaitBlock->WaitListEntry);
|
|||
|
|
|||
|
//
|
|||
|
// If the current thread is processing a queue entry, then attempt
|
|||
|
// to activate another thread that is blocked on the queue object.
|
|||
|
//
|
|||
|
|
|||
|
Queue = Thread->Queue;
|
|||
|
if (Queue != NULL) {
|
|||
|
KiActivateWaiterQueue(Queue);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the thread wait parameters, set the thread dispatcher state
|
|||
|
// to Waiting, and insert the thread in the wait list.
|
|||
|
//
|
|||
|
|
|||
|
Thread->Alertable = Alertable;
|
|||
|
Thread->WaitMode = WaitMode;
|
|||
|
Thread->WaitReason = WaitReason;
|
|||
|
Thread->WaitTime= KiQueryLowTickCount();
|
|||
|
Thread->State = Waiting;
|
|||
|
KiInsertWaitList(WaitMode, Thread);
|
|||
|
|
|||
|
//
|
|||
|
// Switch context to selected thread.
|
|||
|
//
|
|||
|
// Control is returned at the original IRQL.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(KeIsExecutingDpc() == FALSE);
|
|||
|
ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
|
|||
|
|
|||
|
WaitStatus = KiSwapThread();
|
|||
|
|
|||
|
//
|
|||
|
// If the thread was not awakened to deliver a kernel mode APC,
|
|||
|
// then return wait status.
|
|||
|
//
|
|||
|
|
|||
|
if (WaitStatus != STATUS_KERNEL_APC) {
|
|||
|
return WaitStatus;
|
|||
|
}
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(Timeout)) {
|
|||
|
|
|||
|
//
|
|||
|
// Reduce the amount of time remaining before timeout occurs.
|
|||
|
//
|
|||
|
|
|||
|
Timeout = KiComputeWaitInterval(Timer, OriginalTime, &NewTime);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Raise IRQL to DISPATCH_LEVEL and lock the dispatcher database.
|
|||
|
//
|
|||
|
|
|||
|
KiLockDispatcherDatabase(&Thread->WaitIrql);
|
|||
|
} while (TRUE);
|
|||
|
|
|||
|
//
|
|||
|
// The thread is alerted, a user APC should be delivered, or the wait is
|
|||
|
// satisfied. Unlock dispatcher database, lower IRQL to its previous value,
|
|||
|
// and return the wait status.
|
|||
|
//
|
|||
|
|
|||
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|||
|
return WaitStatus;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
KiSetServerWaitClientEvent (
|
|||
|
IN PKEVENT ServerEvent,
|
|||
|
IN PKEVENT ClientEvent,
|
|||
|
IN ULONG WaitMode
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function sets the specified server event and waits on specified
|
|||
|
client event. The wait is performed such that an optimal switch to
|
|||
|
the waiting thread occurs if possible. No timeout is associated with
|
|||
|
the wait, and thus, the issuing thread will wait until the client event
|
|||
|
is signaled or an APC is delivered.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ServerEvent - Supplies a pointer to a dispatcher object of type event.
|
|||
|
|
|||
|
ClientEvent - Supplies a pointer to a dispatcher object of type event.
|
|||
|
|
|||
|
WaitMode - Supplies the processor mode in which the wait is to occur.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The wait completion status. A value of STATUS_SUCCESS is returned if
|
|||
|
the specified object satisfied the wait. A value of STATUS_USER_APC is
|
|||
|
returned if the wait was aborted to deliver a user APC to the current
|
|||
|
thread.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PKTHREAD NextThread;
|
|||
|
KPRIORITY NewPriority;
|
|||
|
LONG OldState;
|
|||
|
PKQUEUE Queue;
|
|||
|
PKTHREAD Thread;
|
|||
|
PKWAIT_BLOCK WaitBlock;
|
|||
|
PLIST_ENTRY WaitEntry;
|
|||
|
|
|||
|
//
|
|||
|
// Raise the IRQL to dispatch level and lock the dispatcher database.
|
|||
|
//
|
|||
|
|
|||
|
Thread = KeGetCurrentThread();
|
|||
|
|
|||
|
ASSERT(Thread->WaitNext == FALSE);
|
|||
|
|
|||
|
KiLockDispatcherDatabase(&Thread->WaitIrql);
|
|||
|
|
|||
|
//
|
|||
|
// If the client event is not in the Signaled state and the server
|
|||
|
// event wait queue is not empty, then attempt to do a direct dispatch
|
|||
|
// to the target thread.
|
|||
|
//
|
|||
|
|
|||
|
if ((ClientEvent->Header.SignalState == 0) &&
|
|||
|
(IsListEmpty(&ServerEvent->Header.WaitListHead) == FALSE)) {
|
|||
|
|
|||
|
//
|
|||
|
// Get the address of the first waiting server thread.
|
|||
|
//
|
|||
|
|
|||
|
WaitEntry = ServerEvent->Header.WaitListHead.Flink;
|
|||
|
WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
|
|||
|
NextThread = WaitBlock->Thread;
|
|||
|
|
|||
|
//
|
|||
|
// Remove the wait block from the event wait list and remove the
|
|||
|
// target thread from the system wait list.
|
|||
|
//
|
|||
|
|
|||
|
RemoveEntryList(&WaitBlock->WaitListEntry);
|
|||
|
RemoveEntryList(&NextThread->WaitListEntry);
|
|||
|
|
|||
|
//
|
|||
|
// If the next thread is processing a queue entry, then increment
|
|||
|
// the current number of threads.
|
|||
|
//
|
|||
|
|
|||
|
Queue = NextThread->Queue;
|
|||
|
if (Queue != NULL) {
|
|||
|
Queue->CurrentCount += 1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Attempt to switch directly to the target thread.
|
|||
|
//
|
|||
|
|
|||
|
return KiSwitchToThread(NextThread, WrEventPair, WaitMode, ClientEvent);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Signal the server event and test to determine if any wait can be
|
|||
|
// satisfied.
|
|||
|
//
|
|||
|
|
|||
|
OldState = ServerEvent->Header.SignalState;
|
|||
|
ServerEvent->Header.SignalState = 1;
|
|||
|
if ((OldState == 0) && (IsListEmpty(&ServerEvent->Header.WaitListHead) == FALSE)) {
|
|||
|
KiWaitTest(ServerEvent, 1);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Continue the event pair wait and return the wait completion status.
|
|||
|
//
|
|||
|
// N.B. The wait continuation routine is called with the dispatcher
|
|||
|
// database locked.
|
|||
|
//
|
|||
|
|
|||
|
return KiContinueClientWait(ClientEvent, WrEventPair, WaitMode);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
KiContinueClientWait (
|
|||
|
IN PVOID ClientObject,
|
|||
|
IN ULONG WaitReason,
|
|||
|
IN ULONG WaitMode
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function continues a wait operation that could not be completed by
|
|||
|
a optimal switch from a client to a server.
|
|||
|
|
|||
|
N.B. This function is entered with the dispatcher database locked.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ClientEvent - Supplies a pointer to a dispatcher object of type event
|
|||
|
or semaphore.
|
|||
|
|
|||
|
WaitReason - Supplies the reason for the wait operation.
|
|||
|
|
|||
|
WaitMode - Supplies the processor mode in which the wait is to occur.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The wait completion status. A value of STATUS_SUCCESS is returned if
|
|||
|
the specified object satisfied the wait. A value of STATUS_USER_APC is
|
|||
|
returned if the wait was aborted to deliver a user APC to the current
|
|||
|
thread.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PKEVENT ClientEvent;
|
|||
|
PRKTHREAD NextThread;
|
|||
|
PRKQUEUE Queue;
|
|||
|
PRKTHREAD Thread;
|
|||
|
PKWAIT_BLOCK WaitBlock;
|
|||
|
NTSTATUS WaitStatus;
|
|||
|
|
|||
|
//
|
|||
|
// Start of wait loop.
|
|||
|
//
|
|||
|
// Note this loop is repeated if a kernel APC is delivered in the middle
|
|||
|
// of the wait or a kernel APC is pending on the first attempt through
|
|||
|
// the loop.
|
|||
|
//
|
|||
|
|
|||
|
ClientEvent = (PKEVENT)ClientObject;
|
|||
|
Thread = KeGetCurrentThread();
|
|||
|
WaitBlock = &Thread->WaitBlock[0];
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// Set address of wait block list in thread object.
|
|||
|
//
|
|||
|
|
|||
|
Thread->WaitBlockList = WaitBlock;
|
|||
|
|
|||
|
//
|
|||
|
// Test to determine if a kernel APC is pending.
|
|||
|
//
|
|||
|
// If a kernel APC is pending and the previous IRQL was less than
|
|||
|
// APC_LEVEL, then a kernel APC was queued by another processor just
|
|||
|
// after IRQL was raised to DISPATCH_LEVEL, but before the dispatcher
|
|||
|
// database was locked.
|
|||
|
//
|
|||
|
// N.B. that this can only happen in a multiprocessor system.
|
|||
|
//
|
|||
|
|
|||
|
if (Thread->ApcState.KernelApcPending && (Thread->WaitIrql < APC_LEVEL)) {
|
|||
|
|
|||
|
//
|
|||
|
// Unlock the dispatcher database and lower IRQL to its previous
|
|||
|
// value. An APC interrupt will immediately occur which will result
|
|||
|
// in the delivery of the kernel APC if possible.
|
|||
|
//
|
|||
|
|
|||
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Test if a user APC is pending.
|
|||
|
//
|
|||
|
|
|||
|
if ((WaitMode != KernelMode) && (Thread->ApcState.UserApcPending)) {
|
|||
|
WaitStatus = STATUS_USER_APC;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the event\semaphore wait block and check to determine
|
|||
|
// if the wait is already satisfied. If the wait is satisfied, then
|
|||
|
// perform wait completion and return. Otherwise, put current thread
|
|||
|
// in a wait state.
|
|||
|
//
|
|||
|
|
|||
|
Thread->WaitStatus = (NTSTATUS)0;
|
|||
|
WaitBlock->Object = ClientEvent;
|
|||
|
WaitBlock->NextWaitBlock = WaitBlock;
|
|||
|
WaitBlock->WaitKey = (CSHORT)(STATUS_SUCCESS);
|
|||
|
WaitBlock->WaitType = WaitAny;
|
|||
|
|
|||
|
//
|
|||
|
// If the signal state is not equal to zero, then satisfy the wait.
|
|||
|
//
|
|||
|
|
|||
|
if (ClientEvent->Header.SignalState != 0) {
|
|||
|
KiWaitSatisfyOther(ClientEvent);
|
|||
|
WaitStatus = (NTSTATUS)(0);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Insert wait block in object wait list.
|
|||
|
//
|
|||
|
|
|||
|
InsertTailList(&ClientEvent->Header.WaitListHead,
|
|||
|
&WaitBlock->WaitListEntry);
|
|||
|
|
|||
|
//
|
|||
|
// If the current thread is processing a queue entry, then attempt
|
|||
|
// to activate another thread that is blocked on the queue object.
|
|||
|
//
|
|||
|
|
|||
|
Queue = Thread->Queue;
|
|||
|
if (Queue != NULL) {
|
|||
|
KiActivateWaiterQueue(Queue);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the thread wait parameters, set the thread dispatcher state
|
|||
|
// to Waiting, and insert the thread in the wait list.
|
|||
|
//
|
|||
|
|
|||
|
Thread->Alertable = FALSE;
|
|||
|
Thread->WaitMode = (KPROCESSOR_MODE)WaitMode;
|
|||
|
Thread->WaitReason = (UCHAR)WaitReason;
|
|||
|
Thread->WaitTime= KiQueryLowTickCount();
|
|||
|
Thread->State = Waiting;
|
|||
|
KiInsertWaitList(WaitMode, Thread);
|
|||
|
|
|||
|
//
|
|||
|
// Switch context to selected thread.
|
|||
|
//
|
|||
|
// Control is returned at the original IRQL.
|
|||
|
//
|
|||
|
|
|||
|
WaitStatus = KiSwapThread();
|
|||
|
|
|||
|
//
|
|||
|
// If the thread was not awakened to deliver a kernel mode APC,
|
|||
|
// then return wait status.
|
|||
|
//
|
|||
|
|
|||
|
if (WaitStatus != STATUS_KERNEL_APC) {
|
|||
|
return WaitStatus;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Raise IRQL to DISPATCH_LEVEL and lock the dispatcher database.
|
|||
|
//
|
|||
|
|
|||
|
KiLockDispatcherDatabase(&Thread->WaitIrql);
|
|||
|
} while (TRUE);
|
|||
|
|
|||
|
//
|
|||
|
// The thread is alerted, a user APC should be delivered, or the wait is
|
|||
|
// satisfied. Unlock dispatcher database, lower IRQL to its previous value,
|
|||
|
// and return the wait status.
|
|||
|
//
|
|||
|
|
|||
|
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
|||
|
return WaitStatus;
|
|||
|
}
|
|||
|
|
|||
|
PLARGE_INTEGER
|
|||
|
FASTCALL
|
|||
|
KiComputeWaitInterval (
|
|||
|
IN PRKTIMER Timer,
|
|||
|
IN PLARGE_INTEGER OriginalTime,
|
|||
|
IN OUT PLARGE_INTEGER NewTime
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function recomputes the wait interval after a thread has been
|
|||
|
awakened to deliver a kernel APC.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Timer - Supplies a pointer to a dispatcher object of type timer.
|
|||
|
|
|||
|
OriginalTime - Supplies a pointer to the original timeout value.
|
|||
|
|
|||
|
NewTime - Supplies a pointer to a variable that receives the
|
|||
|
recomputed wait interval.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
A pointer to the new time is returned as the function value.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
//
|
|||
|
// If the original wait time was absolute, then return the same
|
|||
|
// absolute time. Otherwise, reduce the wait time remaining before
|
|||
|
// the time delay expires.
|
|||
|
//
|
|||
|
|
|||
|
if (Timer->Header.Absolute != FALSE) {
|
|||
|
return OriginalTime;
|
|||
|
|
|||
|
} else {
|
|||
|
KiQueryInterruptTime(NewTime);
|
|||
|
NewTime->QuadPart -= Timer->DueTime.QuadPart;
|
|||
|
return NewTime;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#if !defined(_MIPS_) && !defined(_ALPHA_) && !defined(_PPC_) && !defined(_X86_)
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
KiSwitchToThread (
|
|||
|
IN PKTHREAD NextThread,
|
|||
|
IN ULONG WaitReason,
|
|||
|
IN ULONG WaitMode,
|
|||
|
IN PKEVENT WaitObject
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function performs an optimal switch to the specified target thread
|
|||
|
if possible. No timeout is associated with the wait, thus the issuing
|
|||
|
thread will wait until the wait event is signaled or an APC is deliverd.
|
|||
|
|
|||
|
N.B. This routine is called with the dispatcher database locked.
|
|||
|
|
|||
|
N.B. The wait IRQL is assumed to be set for the current thread and the
|
|||
|
wait status is assumed to be set for the target thread.
|
|||
|
|
|||
|
N.B. It is assumed that if a queue is associated with the target thread,
|
|||
|
then the concurrency count has been incremented.
|
|||
|
|
|||
|
N.B. Control is returned from this function with the dispatcher database
|
|||
|
unlocked.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
NextThread - Supplies a pointer to a dispatcher object of type thread.
|
|||
|
|
|||
|
WaitReason - Supplies the reason for the wait operation.
|
|||
|
|
|||
|
WaitMode - Supplies the processor wait mode.
|
|||
|
|
|||
|
WaitObject - Supplies a pointer to a dispatcher object of type event
|
|||
|
or semaphore.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The wait completion status. A value of STATUS_SUCCESS is returned if
|
|||
|
the specified object satisfied the wait. A value of STATUS_USER_APC is
|
|||
|
returned if the wait was aborted to deliver a user APC to the current
|
|||
|
thread.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
KPRIORITY NewPriority;
|
|||
|
PKPRCB Prcb;
|
|||
|
PKPROCESS Process;
|
|||
|
ULONG Processor;
|
|||
|
PKQUEUE Queue;
|
|||
|
PKTHREAD Thread;
|
|||
|
PKWAIT_BLOCK WaitBlock;
|
|||
|
PLIST_ENTRY WaitEntry;
|
|||
|
PKEVENT WaitForEvent = (PKEVENT)WaitObject;
|
|||
|
NTSTATUS WaitStatus;
|
|||
|
|
|||
|
//
|
|||
|
// If the target thread's kernel stack is resident, the target
|
|||
|
// thread's process is in the balance set, the target thread can
|
|||
|
// can run on the current processor, and another thread has not
|
|||
|
// already been selected to run on the current processor, then
|
|||
|
// do a direct dispatch to the target thread bypassing all the
|
|||
|
// general wait logic, thread priorities permiting.
|
|||
|
//
|
|||
|
|
|||
|
Prcb = KeGetCurrentPrcb();
|
|||
|
Process = NextThread->ApcState.Process;
|
|||
|
Thread = KeGetCurrentThread();
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
|
|||
|
Processor = Thread->NextProcessor;
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
if ((NextThread->KernelStackResident != FALSE) &&
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
|
|||
|
((NextThread->Affinity & (1 << Processor)) != 0) &&
|
|||
|
(Prcb->NextThread == NULL) &&
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
(Process->State == ProcessInMemory)) {
|
|||
|
|
|||
|
//
|
|||
|
// Compute the new thread priority and check if a direct switch
|
|||
|
// to the target thread can be made.
|
|||
|
//
|
|||
|
|
|||
|
if (Thread->Priority < LOW_REALTIME_PRIORITY) {
|
|||
|
if (NextThread->Priority < LOW_REALTIME_PRIORITY) {
|
|||
|
|
|||
|
//
|
|||
|
// Both the current and target threads run at a variable
|
|||
|
// priority level. If the target thread is not running
|
|||
|
// at a boosted level, then attempt to boost its priority
|
|||
|
// to a level that is equal or greater than the current
|
|||
|
// thread.
|
|||
|
//
|
|||
|
|
|||
|
if (NextThread->PriorityDecrement == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// The target thread is not running at a boosted level.
|
|||
|
//
|
|||
|
|
|||
|
NewPriority = NextThread->BasePriority + 1;
|
|||
|
if (NewPriority >= Thread->Priority) {
|
|||
|
if (NewPriority >= LOW_REALTIME_PRIORITY) {
|
|||
|
NextThread->Priority = LOW_REALTIME_PRIORITY - 1;
|
|||
|
|
|||
|
} else {
|
|||
|
NextThread->Priority = (SCHAR)NewPriority;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
if (NextThread->BasePriority >= BASE_PRIORITY_THRESHOLD) {
|
|||
|
NextThread->PriorityDecrement =
|
|||
|
Thread->Priority - NextThread->BasePriority;
|
|||
|
NextThread->DecrementCount = ROUND_TRIP_DECREMENT_COUNT;
|
|||
|
NextThread->Priority = Thread->Priority;
|
|||
|
|
|||
|
} else {
|
|||
|
NextThread->Priority = (SCHAR)NewPriority;
|
|||
|
goto LongWay;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// The target thread is running at a boosted level.
|
|||
|
//
|
|||
|
|
|||
|
NextThread->DecrementCount -= 1;
|
|||
|
if (NextThread->DecrementCount == 0) {
|
|||
|
NextThread->Priority = NextThread->BasePriority;
|
|||
|
NextThread->PriorityDecrement = 0;
|
|||
|
goto LongWay;
|
|||
|
}
|
|||
|
|
|||
|
if (NextThread->Priority < Thread->Priority) {
|
|||
|
goto LongWay;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// The current thread runs at a variable priority level
|
|||
|
// and the target thread runs at a realtime priority
|
|||
|
// level. A direct switch to the target thread can be
|
|||
|
// made.
|
|||
|
//
|
|||
|
|
|||
|
NextThread->Quantum = Process->ThreadQuantum;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// The current thread runs in at a realtime priority level.
|
|||
|
// If the priority of the current thread is less than or
|
|||
|
// equal to the priority of the target thread, then a direct
|
|||
|
// switch to the target thread can be made.
|
|||
|
//
|
|||
|
|
|||
|
if (NextThread->Priority < Thread->Priority) {
|
|||
|
goto LongWay;
|
|||
|
}
|
|||
|
|
|||
|
NextThread->Quantum = Process->ThreadQuantum;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the next processor number.
|
|||
|
//
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
|
|||
|
NextThread->NextProcessor = (CCHAR)Processor;
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Initialization the event wait block and insert the wait block
|
|||
|
// in the wait for event wait list.
|
|||
|
//
|
|||
|
|
|||
|
WaitBlock = &Thread->WaitBlock[0];
|
|||
|
Thread->WaitBlockList = WaitBlock;
|
|||
|
Thread->WaitStatus = (NTSTATUS)0;
|
|||
|
WaitBlock->Object = WaitForEvent;
|
|||
|
WaitBlock->NextWaitBlock = WaitBlock;
|
|||
|
WaitBlock->WaitKey = (CSHORT)(STATUS_SUCCESS);
|
|||
|
WaitBlock->WaitType = WaitAny;
|
|||
|
InsertTailList(&WaitForEvent->Header.WaitListHead,
|
|||
|
&WaitBlock->WaitListEntry);
|
|||
|
|
|||
|
//
|
|||
|
// If the current thread is processing a queue entry, then attempt
|
|||
|
// to activate another thread that is blocked on the queue object.
|
|||
|
//
|
|||
|
|
|||
|
Queue = Thread->Queue;
|
|||
|
Prcb->NextThread = NextThread;
|
|||
|
if (Queue != NULL) {
|
|||
|
KiActivateWaiterQueue(Queue);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the current thread wait parameters, set the thread state
|
|||
|
// to Waiting, and insert the thread in the wait list.
|
|||
|
//
|
|||
|
// N.B. It is not necessary to increment and decrement the wait
|
|||
|
// reason count since both the server and the client have
|
|||
|
// the same wait reason.
|
|||
|
//
|
|||
|
|
|||
|
Thread->Alertable = FALSE;
|
|||
|
Thread->WaitMode = (KPROCESSOR_MODE)WaitMode;
|
|||
|
Thread->WaitReason = (UCHAR)WaitReason;
|
|||
|
Thread->WaitTime= KiQueryLowTickCount();
|
|||
|
Thread->State = Waiting;
|
|||
|
KiInsertWaitList(WaitMode, Thread);
|
|||
|
|
|||
|
//
|
|||
|
// Switch context to target thread.
|
|||
|
//
|
|||
|
// Control is returned at the original IRQL.
|
|||
|
//
|
|||
|
|
|||
|
WaitStatus = KiSwapThread();
|
|||
|
|
|||
|
//
|
|||
|
// If the thread was not awakened to deliver a kernel mode APC,
|
|||
|
// then return wait status.
|
|||
|
//
|
|||
|
|
|||
|
if (WaitStatus != STATUS_KERNEL_APC) {
|
|||
|
return WaitStatus;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Raise IRQL to DISPATCH_LEVEL and lock the dispatcher database.
|
|||
|
//
|
|||
|
|
|||
|
KiLockDispatcherDatabase(&Thread->WaitIrql);
|
|||
|
goto ContinueWait;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Ready the target thrread for execution and wait on the specified
|
|||
|
// object.
|
|||
|
//
|
|||
|
|
|||
|
LongWay:
|
|||
|
|
|||
|
KiReadyThread(NextThread);
|
|||
|
|
|||
|
//
|
|||
|
// Continue the wait and return the wait completion status.
|
|||
|
//
|
|||
|
// N.B. The wait continuation routine is called with the dispatcher
|
|||
|
// database locked.
|
|||
|
//
|
|||
|
|
|||
|
ContinueWait:
|
|||
|
|
|||
|
return KiContinueClientWait(WaitForEvent, WaitReason, WaitMode);
|
|||
|
}
|
|||
|
|
|||
|
#endif
|