BOOLEAN
NTAPI
KeTestAlertThread(IN KPROCESSOR_MODE AlertMode)
{
PKTHREAD Thread = KeGetCurrentThread();
BOOLEAN OldState;
KLOCK_QUEUE_HANDLE ApcLock;
ASSERT_THREAD(Thread);
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
/* Lock the Dispatcher Database and the APC Queue */
KiAcquireApcLock(Thread, &ApcLock);
/* Save the old State */
OldState = Thread->Alerted[AlertMode];
/* Check the Thread is alerted */
if (OldState)
{
/* Disable alert for this mode */
Thread->Alerted[AlertMode] = FALSE;
}
else if ((AlertMode != KernelMode) &&
(!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])))
{
/* If the mode is User and the Queue isn't empty, set Pending */
Thread->ApcState.UserApcPending = TRUE; // 然后返回用户空间时就立即执行了
}
/* Release Locks and return the Old State */
KiReleaseApcLock(&ApcLock);
return OldState;
}
NTSTATUS
NTAPI
KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode,
IN BOOLEAN Alertable,
IN PLARGE_INTEGER Interval OPTIONAL)
{
PKTIMER Timer;
PKWAIT_BLOCK TimerBlock;
PKTHREAD Thread = KeGetCurrentThread();
NTSTATUS WaitStatus;
BOOLEAN Swappable;
PLARGE_INTEGER OriginalDueTime;
LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
ULONG Hand = 0;
if (Thread->WaitNext)
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
else
ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
/* If this is a user-mode wait of 0 seconds, yield execution */
if (!(Interval->QuadPart) && (WaitMode != KernelMode))
{
/* Make sure the wait isn't alertable or interrupting an APC */
if (!(Alertable) && !(Thread->ApcState.UserApcPending))
{
/* Yield execution */
return NtYieldExecution();
}
}
/* Setup the original time and timer/wait blocks */
OriginalDueTime = Interval;
Timer = &Thread->Timer;
TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
/* Check if the lock is already held */
if (!Thread->WaitNext) goto WaitStart;
/* Otherwise, we already have the lock, so initialize the wait */
Thread->WaitNext = FALSE;
KxDelayThreadWait();
/* Start wait loop */
for (;;)
{
/* Disable pre-emption */
Thread->Preempted = FALSE;
/* Check if a kernel APC is pending and we're below APC_LEVEL */
if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) &&
(Thread->WaitIrql < APC_LEVEL))
{
/* Unlock the dispatcher */
KiReleaseDispatcherLock(Thread->WaitIrql);
}
else
{
/* Check if we have to bail out due to an alerted state */
WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode); // Alertable为TRUE以及UserMode时置位
if (WaitStatus != STATUS_WAIT_0) break;
/* Check if the timer expired */
InterruptTime.QuadPart = KeQueryInterruptTime();
if ((ULONGLONG)InterruptTime.QuadPart >= Timer->DueTime.QuadPart)
{
/* It did, so we don't need to wait */
goto NoWait;
}
/* It didn't, so activate it */
Timer->Header.Inserted = TRUE;
/* Handle Kernel Queues */
if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
/* Setup the wait information */
Thread->State = Waiting;
/* Add the thread to the wait list */
KiAddThreadToWaitList(Thread, Swappable);
/* Insert the timer and swap the thread */
ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
KiSetThreadSwapBusy(Thread);
KxInsertTimer(Timer, Hand);
WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb());
/* Check if were swapped ok */
if (WaitStatus != STATUS_KERNEL_APC)
{
/* This is a good thing */
if (WaitStatus == STATUS_TIMEOUT) WaitStatus = STATUS_SUCCESS;
/* Return Status */
return WaitStatus;
}
/* Recalculate due times */
Interval = KiRecalculateDueTime(OriginalDueTime,
&DueTime,
&NewDueTime);
}
WaitStart:
/* Setup a new wait */
Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
KxDelayThreadWait();
KiAcquireDispatcherLockAtDpcLevel();
}
/* We're done! */
KiReleaseDispatcherLock(Thread->WaitIrql);
return WaitStatus;
NoWait:
/* There was nothing to wait for. Did we have a wait interval? */
if (!Interval->QuadPart)
{
/* Unlock the dispatcher and do a yield */
KiReleaseDispatcherLock(Thread->WaitIrql);
return NtYieldExecution();
}
/* Unlock the dispatcher and adjust the quantum for a no-wait */
KiReleaseDispatcherLockFromDpcLevel();
KiAdjustQuantumThread(Thread);
return STATUS_SUCCESS;
}
FORCEINLINE
NTSTATUS
KiCheckAlertability(IN PKTHREAD Thread,
IN BOOLEAN Alertable,
IN KPROCESSOR_MODE WaitMode)
{
/* Check if the wait is alertable */
if (Alertable)
{
/* It is, first check if the thread is alerted in this mode */
if (Thread->Alerted[WaitMode])
{
/* It is, so bail out of the wait */
Thread->Alerted[WaitMode] = FALSE;
return STATUS_ALERTED;
}
else if ((WaitMode != KernelMode) &&
(!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])))
{
/* It's isn't, but this is a user wait with queued user APCs */
Thread->ApcState.UserApcPending = TRUE; // 前提是队列里一定有未决APC
return STATUS_USER_APC;
}
else if (Thread->Alerted[KernelMode])
{
/* It isn't that either, but we're alered in kernel mode */
Thread->Alerted[KernelMode] = FALSE;
return STATUS_ALERTED;
}
}
else if ((WaitMode != KernelMode) && (Thread->ApcState.UserApcPending))
{
/* Not alertable, but this is a user wait with pending user APCs */
return STATUS_USER_APC;
}
/* Otherwise, we're fine */
return STATUS_WAIT_0;
}