Example #1
0
VOID
NTAPI
KiAdjustQuantumThread(IN PKTHREAD Thread)
{
    PKPRCB Prcb = KeGetCurrentPrcb();
    PKTHREAD NextThread;

    /* Acquire thread and PRCB lock */
    KiAcquireThreadLock(Thread);
    KiAcquirePrcbLock(Prcb);

    /* Don't adjust for RT threads */
    if ((Thread->Priority < LOW_REALTIME_PRIORITY) &&
            (Thread->BasePriority < (LOW_REALTIME_PRIORITY - 2)))
    {
        /* Decrease Quantum by one and see if we've ran out */
        if (--Thread->Quantum <= 0)
        {
            /* Return quantum */
            Thread->Quantum = Thread->QuantumReset;

            /* Calculate new Priority */
            Thread->Priority = KiComputeNewPriority(Thread, 1);

            /* Check if there's no next thread scheduled */
            if (!Prcb->NextThread)
            {
                /* Select a ready thread and check if we found one */
                NextThread = KiSelectReadyThread(Thread->Priority, Prcb);
                if (NextThread)
                {
                    /* Set it on standby and switch to it */
                    NextThread->State = Standby;
                    Prcb->NextThread = NextThread;
                }
            }
            else
            {
                /* This thread can be preempted again */
                Thread->Preempted = FALSE;
            }
        }
    }

    /* Release locks */
    KiReleasePrcbLock(Prcb);
    KiReleaseThreadLock(Thread);
    KiExitDispatcher(Thread->WaitIrql);
}
Example #2
0
File: apc.c Project: GYGit/reactos
/*++
 * @name KiInsertQueueApc
 *
 *     The KiInsertQueueApc routine queues a APC for execution when the right
 *     scheduler environment exists.
 *
 * @param Apc
 *        Pointer to an initialized control object of type APC for which the
 *        caller provides the storage.
 *
 * @param PriorityBoost
 *        Priority Boost to apply to the Thread.
 *
 * @return None
 *
 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered,
 *          and at PASSIVE_LEVEL for the NormalRoutine registered.
 *
 *          Callers of this routine must have locked the dipatcher database.
 *
 *--*/
VOID
FASTCALL
KiInsertQueueApc(IN PKAPC Apc,
                 IN KPRIORITY PriorityBoost)
{
    PKTHREAD Thread = Apc->Thread;
    PKAPC_STATE ApcState;
    KPROCESSOR_MODE ApcMode;
    PLIST_ENTRY ListHead, NextEntry;
    PKAPC QueuedApc;
    PKGATE Gate;
    NTSTATUS Status;
    BOOLEAN RequestInterrupt = FALSE;

    /*
     * Check if the caller wanted this APC to use the thread's environment at
     * insertion time.
     */
    if (Apc->ApcStateIndex == InsertApcEnvironment)
    {
        /* Copy it over */
        Apc->ApcStateIndex = Thread->ApcStateIndex;
    }

    /* Get the APC State for this Index, and the mode too */
    ApcState = Thread->ApcStatePointer[(UCHAR)Apc->ApcStateIndex];
    ApcMode = Apc->ApcMode;

    /* The APC must be "inserted" already */
    ASSERT(Apc->Inserted == TRUE);

    /* Three scenarios:
     * 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List
     * 2) User APC which is PsExitSpecialApc = Put it at the front of the List
     * 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list
     */
    if (Apc->NormalRoutine)
    {
        /* Normal APC; is it the Thread Termination APC? */
        if ((ApcMode != KernelMode) &&
            (Apc->KernelRoutine == PsExitSpecialApc))
        {
            /* Set User APC pending to true */
            Thread->ApcState.UserApcPending = TRUE;

            /* Insert it at the top of the list */
            InsertHeadList(&ApcState->ApcListHead[ApcMode],
                           &Apc->ApcListEntry);
        }
        else
        {
            /* Regular user or kernel Normal APC */
            InsertTailList(&ApcState->ApcListHead[ApcMode],
                           &Apc->ApcListEntry);
        }
    }
    else
    {
        /* Special APC, find the last one in the list */
        ListHead = &ApcState->ApcListHead[ApcMode];
        NextEntry = ListHead->Blink;
        while (NextEntry != ListHead)
        {
            /* Get the APC */
            QueuedApc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);

            /* Is this a No-Normal APC? If so, break */
            if (!QueuedApc->NormalRoutine) break;

            /* Move to the previous APC in the Queue */
            NextEntry = NextEntry->Blink;
        }

        /* Insert us here */
        InsertHeadList(NextEntry, &Apc->ApcListEntry);
    }

    /* Now check if the Apc State Indexes match */
    if (Thread->ApcStateIndex == Apc->ApcStateIndex)
    {
        /* Check that the thread matches */
        if (Thread == KeGetCurrentThread())
        {
            /* Sanity check */
            ASSERT(Thread->State == Running);

            /* Check if this is kernel mode */
            if (ApcMode == KernelMode)
            {
                /* All valid, a Kernel APC is pending now */
                Thread->ApcState.KernelApcPending = TRUE;

                /* Check if Special APCs are disabled */
                if (!Thread->SpecialApcDisable)
                {
                    /* They're not, so request the interrupt */
                    HalRequestSoftwareInterrupt(APC_LEVEL);
                }
            }
        }
        else
        {
            /* Acquire the dispatcher lock */
            KiAcquireDispatcherLock();

            /* Check if this is a kernel-mode APC */
            if (ApcMode == KernelMode)
            {
                /* Kernel-mode APC, set us pending */
                Thread->ApcState.KernelApcPending = TRUE;

                /* Are we currently running? */
                if (Thread->State == Running)
                {
                    /* The thread is running, so remember to send a request */
                    RequestInterrupt = TRUE;
                }
                else if ((Thread->State == Waiting) &&
                         (Thread->WaitIrql == PASSIVE_LEVEL) &&
                         !(Thread->SpecialApcDisable) &&
                         (!(Apc->NormalRoutine) ||
                          (!(Thread->KernelApcDisable) &&
                           !(Thread->ApcState.KernelApcInProgress))))
                {
                    /* We'll unwait with this status */
                    Status = STATUS_KERNEL_APC;

                    /* Wake up the thread */
                    KiUnwaitThread(Thread, Status, PriorityBoost);
                }
                else if (Thread->State == GateWait)
                {
                    /* Lock the thread */
                    KiAcquireThreadLock(Thread);
                    
                    /* Essentially do the same check as above */
                    if ((Thread->State == GateWait) &&
                        (Thread->WaitIrql == PASSIVE_LEVEL) &&
                        !(Thread->SpecialApcDisable) &&
                        (!(Apc->NormalRoutine) ||
                         (!(Thread->KernelApcDisable) &&
                          !(Thread->ApcState.KernelApcInProgress))))
                    {
                        /* We were in a gate wait. Handle this. */
                        DPRINT1("A thread was in a gate wait\n");
                        
                        /* Get the gate */
                        Gate = Thread->GateObject;

                        /* Lock the gate */
                        KiAcquireDispatcherObject(&Gate->Header);
                        
                        /* Remove it from the waiters list */
                        RemoveEntryList(&Thread->WaitBlock[0].WaitListEntry);
                        
                        /* Unlock the gate */
                        KiReleaseDispatcherObject(&Gate->Header);
                        
                        /* Increase the queue counter if needed */
                        if (Thread->Queue) Thread->Queue->CurrentCount++;
                        
                        /* Put into deferred ready list with this status */
                        Thread->WaitStatus = STATUS_KERNEL_APC;
                        KiInsertDeferredReadyList(Thread);
                    }
                    
                    /* Release the thread lock */
                    KiReleaseThreadLock(Thread);
                }
            }
            else if ((Thread->State == Waiting) &&
                     (Thread->WaitMode == UserMode) &&
                     ((Thread->Alertable) ||
                      (Thread->ApcState.UserApcPending)))
            {
                /* Set user-mode APC pending */
                Thread->ApcState.UserApcPending = TRUE;
                Status = STATUS_USER_APC;

                /* Wake up the thread */
                KiUnwaitThread(Thread, Status, PriorityBoost);
            }

            /* Release dispatcher lock */
            KiReleaseDispatcherLockFromDpcLevel();

            /* Check if an interrupt was requested */
            KiRequestApcInterrupt(RequestInterrupt, Thread->NextProcessor);
        }
    }
}
Example #3
0
VOID
NTAPI
KiQuantumEnd(VOID)
{
    PKPRCB Prcb = KeGetCurrentPrcb();
    PKTHREAD NextThread, Thread = Prcb->CurrentThread;

    /* Check if a DPC Event was requested to be signaled */
    if (InterlockedExchange(&Prcb->DpcSetEventRequest, 0))
    {
        /* Signal it */
        KeSetEvent(&Prcb->DpcEvent, 0, 0);
    }

    /* Raise to synchronization level and lock the PRCB and thread */
    KeRaiseIrqlToSynchLevel();
    KiAcquireThreadLock(Thread);
    KiAcquirePrcbLock(Prcb);

    /* Check if Quantum expired */
    if (Thread->Quantum <= 0)
    {
        /* Check if we're real-time and with quantums disabled */
        if ((Thread->Priority >= LOW_REALTIME_PRIORITY) &&
            (Thread->ApcState.Process->DisableQuantum))
        {
            /* Otherwise, set maximum quantum */
            Thread->Quantum = MAX_QUANTUM;
        }
        else
        {
            /* Reset the new Quantum */
            Thread->Quantum = Thread->QuantumReset;

            /* Calculate new priority */
            Thread->Priority = KiComputeNewPriority(Thread, 1);

            /* Check if a new thread is scheduled */
            if (!Prcb->NextThread)
            {
                /* Get a new ready thread */
                NextThread = KiSelectReadyThread(Thread->Priority, Prcb);
                if (NextThread)
                {
                    /* Found one, set it on standby */
                    NextThread->State = Standby;
                    Prcb->NextThread = NextThread;
                }
            }
            else
            {
                /* Otherwise, make sure that this thread doesn't get preempted */
                Thread->Preempted = FALSE;
            }
        }
    }

    /* Release the thread lock */
    KiReleaseThreadLock(Thread);

    /* Check if there's no thread scheduled */
    if (!Prcb->NextThread)
    {
        /* Just leave now */
        KiReleasePrcbLock(Prcb);
        KeLowerIrql(DISPATCH_LEVEL);
        return;
    }

    /* Get the next thread now */
    NextThread = Prcb->NextThread;

    /* Set current thread's swap busy to true */
    KiSetThreadSwapBusy(Thread);

    /* Switch threads in PRCB */
    Prcb->NextThread = NULL;
    Prcb->CurrentThread = NextThread;

    /* Set thread to running and the switch reason to Quantum End */
    NextThread->State = Running;
    Thread->WaitReason = WrQuantumEnd;

    /* Queue it on the ready lists */
    KxQueueReadyThread(Thread, Prcb);

    /* Set wait IRQL to APC_LEVEL */
    Thread->WaitIrql = APC_LEVEL;

    /* Swap threads */
    KiSwapContext(APC_LEVEL, Thread);

    /* Lower IRQL back to DISPATCH_LEVEL */
    KeLowerIrql(DISPATCH_LEVEL);
}
Example #4
0
VOID
FASTCALL
KiDeferredReadyThread(IN PKTHREAD Thread)
{
    PKPRCB Prcb;
    BOOLEAN Preempted;
    ULONG Processor = 0;
    KPRIORITY OldPriority;
    PKTHREAD NextThread;

    /* Sanity checks */
    ASSERT(Thread->State == DeferredReady);
    ASSERT((Thread->Priority >= 0) && (Thread->Priority <= HIGH_PRIORITY));

    /* Check if we have any adjusts to do */
    if (Thread->AdjustReason == AdjustBoost)
    {
        /* Lock the thread */
        KiAcquireThreadLock(Thread);

        /* Check if the priority is low enough to qualify for boosting */
        if ((Thread->Priority <= Thread->AdjustIncrement) &&
                (Thread->Priority < (LOW_REALTIME_PRIORITY - 3)) &&
                !(Thread->DisableBoost))
        {
            /* Calculate the new priority based on the adjust increment */
            OldPriority = min(Thread->AdjustIncrement + 1,
                              LOW_REALTIME_PRIORITY - 3);

            /* Make sure we're not decreasing outside of the priority range */
            ASSERT((Thread->PriorityDecrement >= 0) &&
                   (Thread->PriorityDecrement <= Thread->Priority));

            /* Calculate the new priority decrement based on the boost */
            Thread->PriorityDecrement += ((SCHAR)OldPriority - Thread->Priority);

            /* Again verify that this decrement is valid */
            ASSERT((Thread->PriorityDecrement >= 0) &&
                   (Thread->PriorityDecrement <= OldPriority));

            /* Set the new priority */
            Thread->Priority = (SCHAR)OldPriority;
        }

        /* We need 4 quanta, make sure we have them, then decrease by one */
        if (Thread->Quantum < 4) Thread->Quantum = 4;
        Thread->Quantum--;

        /* Make sure the priority is still valid */
        ASSERT((Thread->Priority >= 0) && (Thread->Priority <= HIGH_PRIORITY));

        /* Release the lock and clear the adjust reason */
        KiReleaseThreadLock(Thread);
        Thread->AdjustReason = AdjustNone;
    }
    else if (Thread->AdjustReason == AdjustUnwait)
    {
        /* Acquire the thread lock and check if this is a real-time thread */
        KiAcquireThreadLock(Thread);
        if (Thread->Priority < LOW_REALTIME_PRIORITY)
        {
            /* It's not real time, but is it time critical? */
            if (Thread->BasePriority >= (LOW_REALTIME_PRIORITY - 2))
            {
                /* It is, so simply reset its quantum */
                Thread->Quantum = Thread->QuantumReset;
            }
            else
            {
                /* Has the priority been adjusted previously? */
                if (!(Thread->PriorityDecrement) && (Thread->AdjustIncrement))
                {
                    /* Yes, reset its quantum */
                    Thread->Quantum = Thread->QuantumReset;
                }

                /* Wait code already handles quantum adjustment during APCs */
                if (Thread->WaitStatus != STATUS_KERNEL_APC)
                {
                    /* Decrease the quantum by one and check if we're out */
                    if (--Thread->Quantum <= 0)
                    {
                        /* We are, reset the quantum and get a new priority */
                        Thread->Quantum = Thread->QuantumReset;
                        Thread->Priority = KiComputeNewPriority(Thread, 1);
                    }
                }
            }

            /* Now check if we have no decrement and boosts are enabled */
            if (!(Thread->PriorityDecrement) && !(Thread->DisableBoost))
            {
                /* Make sure we have an increment */
                ASSERT(Thread->AdjustIncrement >= 0);

                /* Calculate the new priority after the increment */
                OldPriority = Thread->BasePriority + Thread->AdjustIncrement;

                /* Check if this new priority is higher */
                if (OldPriority > Thread->Priority)
                {
                    /* Make sure we don't go into the real time range */
                    if (OldPriority >= LOW_REALTIME_PRIORITY)
                    {
                        /* Normalize it back down one notch */
                        OldPriority = LOW_REALTIME_PRIORITY - 1;
                    }

                    /* Check if the priority is higher then the boosted base */
                    if (OldPriority > (Thread->BasePriority +
                                       Thread->AdjustIncrement))
                    {
                        /* Setup a priority decrement to nullify the boost  */
                        Thread->PriorityDecrement = ((SCHAR)OldPriority -
                                                     Thread->BasePriority -
                                                     Thread->AdjustIncrement);
                    }

                    /* Make sure that the priority decrement is valid */
                    ASSERT((Thread->PriorityDecrement >= 0) &&
                           (Thread->PriorityDecrement <= OldPriority));

                    /* Set this new priority */
                    Thread->Priority = (SCHAR)OldPriority;
                }
            }
        }
        else
        {
            /* It's a real-time thread, so just reset its quantum */
            Thread->Quantum = Thread->QuantumReset;
        }

        /* Make sure the priority makes sense */
        ASSERT((Thread->Priority >= 0) && (Thread->Priority <= HIGH_PRIORITY));

        /* Release the thread lock and reset the adjust reason */
        KiReleaseThreadLock(Thread);
        Thread->AdjustReason = AdjustNone;
    }

    /* Clear thread preemption status and save current values */
    Preempted = Thread->Preempted;
    OldPriority = Thread->Priority;
    Thread->Preempted = FALSE;

    /* Queue the thread on CPU 0 and get the PRCB and lock it */
    Thread->NextProcessor = 0;
    Prcb = KiProcessorBlock[0];
    KiAcquirePrcbLock(Prcb);

    /* Check if we have an idle summary */
    if (KiIdleSummary)
    {
        /* Clear it and set this thread as the next one */
        KiIdleSummary = 0;
        Thread->State = Standby;
        Prcb->NextThread = Thread;

        /* Unlock the PRCB and return */
        KiReleasePrcbLock(Prcb);
        return;
    }

    /* Set the CPU number */
    Thread->NextProcessor = (UCHAR)Processor;

    /* Get the next scheduled thread */
    NextThread = Prcb->NextThread;
    if (NextThread)
    {
        /* Sanity check */
        ASSERT(NextThread->State == Standby);

        /* Check if priority changed */
        if (OldPriority > NextThread->Priority)
        {
            /* Preempt the thread */
            NextThread->Preempted = TRUE;

            /* Put this one as the next one */
            Thread->State = Standby;
            Prcb->NextThread = Thread;

            /* Set it in deferred ready mode */
            NextThread->State = DeferredReady;
            NextThread->DeferredProcessor = Prcb->Number;
            KiReleasePrcbLock(Prcb);
            KiDeferredReadyThread(NextThread);
            return;
        }
    }
    else
    {
        /* Set the next thread as the current thread */
        NextThread = Prcb->CurrentThread;
        if (OldPriority > NextThread->Priority)
        {
            /* Preempt it if it's already running */
            if (NextThread->State == Running) NextThread->Preempted = TRUE;

            /* Set the thread on standby and as the next thread */
            Thread->State = Standby;
            Prcb->NextThread = Thread;

            /* Release the lock */
            KiReleasePrcbLock(Prcb);

            /* Check if we're running on another CPU */
            if (KeGetCurrentProcessorNumber() != Thread->NextProcessor)
            {
                /* We are, send an IPI */
                KiIpiSend(AFFINITY_MASK(Thread->NextProcessor), IPI_DPC);
            }
            return;
        }
    }

    /* Sanity check */
    ASSERT((OldPriority >= 0) && (OldPriority <= HIGH_PRIORITY));

    /* Set this thread as ready */
    Thread->State = Ready;
    Thread->WaitTime = KeTickCount.LowPart;

    /* Insert this thread in the appropriate order */
    Preempted ? InsertHeadList(&Prcb->DispatcherReadyListHead[OldPriority],
                               &Thread->WaitListEntry) :
    InsertTailList(&Prcb->DispatcherReadyListHead[OldPriority],
                   &Thread->WaitListEntry);

    /* Update the ready summary */
    Prcb->ReadySummary |= PRIORITY_MASK(OldPriority);

    /* Sanity check */
    ASSERT(OldPriority == Thread->Priority);

    /* Release the lock */
    KiReleasePrcbLock(Prcb);
}
Example #5
0
/*
 * @implemented
 */
NTSTATUS
NTAPI
NtYieldExecution(VOID)
{
    NTSTATUS Status;
    KIRQL OldIrql;
    PKPRCB Prcb;
    PKTHREAD Thread, NextThread;

    /* NB: No instructions (other than entry code) should preceed this line */

    /* Fail if there's no ready summary */
    if (!KiGetCurrentReadySummary()) return STATUS_NO_YIELD_PERFORMED;

    /* Now get the current thread, set the status... */
    Status = STATUS_NO_YIELD_PERFORMED;
    Thread = KeGetCurrentThread();

    /* Raise IRQL to synch and get the KPRCB now */
    OldIrql = KeRaiseIrqlToSynchLevel();
    Prcb = KeGetCurrentPrcb();

    /* Now check if there's still a ready summary */
    if (Prcb->ReadySummary)
    {
        /* Acquire thread and PRCB lock */
        KiAcquireThreadLock(Thread);
        KiAcquirePrcbLock(Prcb);

        /* Find a new thread to run if none was selected */
        if (!Prcb->NextThread) Prcb->NextThread = KiSelectReadyThread(1, Prcb);

        /* Make sure we still have a next thread to schedule */
        NextThread = Prcb->NextThread;
        if (NextThread)
        {
            /* Reset quantum and recalculate priority */
            Thread->Quantum = Thread->QuantumReset;
            Thread->Priority = KiComputeNewPriority(Thread, 1);

            /* Release the thread lock */
            KiReleaseThreadLock(Thread);

            /* Set context swap busy */
            KiSetThreadSwapBusy(Thread);

            /* Set the new thread as running */
            Prcb->NextThread = NULL;
            Prcb->CurrentThread = NextThread;
            NextThread->State = Running;

            /* Setup a yield wait and queue the thread */
            Thread->WaitReason = WrYieldExecution;
            KxQueueReadyThread(Thread, Prcb);

            /* Make it wait at APC_LEVEL */
            Thread->WaitIrql = APC_LEVEL;

            /* Sanity check */
            ASSERT(OldIrql <= DISPATCH_LEVEL);

            /* Swap to new thread */
            KiSwapContext(APC_LEVEL, Thread);
            Status = STATUS_SUCCESS;
        }
        else
        {
            /* Release the PRCB and thread lock */
            KiReleasePrcbLock(Prcb);
            KiReleaseThreadLock(Thread);
        }
    }

    /* Lower IRQL and return */
    KeLowerIrql(OldIrql);
    return Status;
}
Example #6
0
NTSTATUS
NtYieldExecution (
    VOID
    )

/*++

Routine Description:

    This function yields execution to any ready thread for up to one
    quantum.

Arguments:

    None.

Return Value:

    None.

--*/

{

    KIRQL OldIrql;
    PKTHREAD NewThread;
    PRKPRCB Prcb;
    NTSTATUS Status;
    PKTHREAD Thread;

    //
    // If no other threads are ready, then return immediately. Otherwise,
    // attempt to yield execution.
    //
    // N.B. The test for ready threads is made outside any synchonization.
    //      Since this code cannot be perfectly synchronized under any
    //      conditions the lack of synchronization is of no consequence.
    //      

    if (KiGetCurrentReadySummary() == 0) {
        return STATUS_NO_YIELD_PERFORMED;

    } else {
        Status = STATUS_NO_YIELD_PERFORMED;
        Thread = KeGetCurrentThread();
        OldIrql = KeRaiseIrqlToSynchLevel();
        Prcb = KeGetCurrentPrcb();
        if (Prcb->ReadySummary != 0) {
    
            //
            // Acquire the thread lock and the PRCB lock.
            //
            // If a thread has not already been selected for execution, then
            // attempt to select another thread for execution.
            //
    
            KiAcquireThreadLock(Thread);
            KiAcquirePrcbLock(Prcb);
            if (Prcb->NextThread == NULL) {
                Prcb->NextThread = KiSelectReadyThread(1, Prcb);
            }
    
            //
            // If a new thread has been selected for execution, then switch
            // immediately to the selected thread.
            //
    
            if ((NewThread = Prcb->NextThread) != NULL) {
                Thread->Quantum = Thread->QuantumReset;
    
                //
                // Compute the new thread priority.
                //
                // N.B. The new priority will never be greater than the previous
                //      priority.
                //
    
                Thread->Priority = KiComputeNewPriority(Thread, 1);
    
                //
                // Release the thread lock, set swap busy for the old thread,
                // set the next thread to NULL, set the current thread to the
                // new thread, set the new thread state to running, set the
                // wait reason, queue the old running thread, and release the
                // PRCB lock, and swp context to the new thread.
                //
    
                KiReleaseThreadLock(Thread);
                KiSetContextSwapBusy(Thread);
                Prcb->NextThread = NULL;
                Prcb->CurrentThread = NewThread;
                NewThread->State = Running;
                Thread->WaitReason = WrYieldExecution;
                KxQueueReadyThread(Thread, Prcb);
                Thread->WaitIrql = APC_LEVEL;
    
                ASSERT(OldIrql <= DISPATCH_LEVEL);
    
                KiSwapContext(Thread, NewThread);
                Status = STATUS_SUCCESS;
    
            } else {
                KiReleasePrcbLock(Prcb);
                KiReleaseThreadLock(Thread);
            }
        }
    
        //
        // Lower IRQL to its previous level and return.
        //
    
        KeLowerIrql(OldIrql);
        return Status;
    }
}
Example #7
0
VOID
FASTCALL
KeWaitForGate (
    __inout PKGATE Gate,
    __in KWAIT_REASON WaitReason,
    __in KPROCESSOR_MODE WaitMode
    )

/*++

Routine Description:

    This function waits until the signal state of a gate object is set. If
    the state of the gate object is signaled when the wait is executed, then
    no wait will occur.

Arguments:

    Gate - Supplies a pointer to a dispatcher object of type Gate.

    WaitReason - Supplies the reason for the wait.

    WaitMode  - Supplies the processor mode in which the wait is to occur.

Return Value:

    None.

--*/

{

    PKTHREAD CurrentThread;
    KLOCK_QUEUE_HANDLE LockHandle;
    PKQUEUE Queue;
    PKWAIT_BLOCK WaitBlock;
    NTSTATUS WaitStatus;

    ASSERT_GATE(Gate);

    ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);

    //
    // Raise IRQL to SYNCH_LEVEL and acquire the APC queue lock.
    //

    CurrentThread = KeGetCurrentThread();
    do {
        KeAcquireInStackQueuedSpinLockRaiseToSynch(&CurrentThread->ApcQueueLock,
                                                   &LockHandle);

        //
        // Test to determine if a kernel APC is pending.
        //
        // If a kernel APC is pending, the special APC disable count is zero,
        // and the previous IRQL was less than APC_LEVEL, then a kernel APC
        // was queued by another processor just after IRQL was raised to
        // SYNCH_LEVEL, but before the APC queue lock was acquired.
        //
        // N.B. This can only happen in a multiprocessor system.
        //

        if (CurrentThread->ApcState.KernelApcPending &&
            (CurrentThread->SpecialApcDisable == 0) &&
            (LockHandle.OldIrql < APC_LEVEL)) {

            //
            // Unlock the APC queue lock 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.
            //

            KeReleaseInStackQueuedSpinLock(&LockHandle);
            continue;
        }

        //
        // If the current thread is associated with a queue object, then
        // acquire the dispatcher lock.
        //

        if ((Queue = CurrentThread->Queue) != NULL) {
            KiLockDispatcherDatabaseAtSynchLevel();
        }

        //
        // Acquire the thread lock and the object lock.
        //
        // If the object is already signaled, then clear the signaled state,
        // release the object lock, release the thread lock, and lower IRQL
        // to its previous value. Otherwise, set the thread state to gate
        // wait, set the address of the gate object, insert the thread in the
        // object wait list, set context swap busy, release the object lock,
        // release the thread lock, and switch to a new thread.
        //

        KiAcquireThreadLock(CurrentThread);
        KiAcquireKobjectLock(Gate);
        if (Gate->Header.SignalState != 0) {
            Gate->Header.SignalState = 0;
            KiReleaseKobjectLock(Gate);
            KiReleaseThreadLock(CurrentThread);
            if (Queue != NULL) {
                KiUnlockDispatcherDatabaseFromSynchLevel();
            }

            KeReleaseInStackQueuedSpinLock(&LockHandle);
            break;
    
        } else {
            WaitBlock = &CurrentThread->WaitBlock[0];
            WaitBlock->Object = Gate;
            WaitBlock->Thread = CurrentThread;
            CurrentThread->WaitMode = WaitMode;
            CurrentThread->WaitReason = WaitReason;
            CurrentThread->WaitIrql = LockHandle.OldIrql;
            CurrentThread->State = GateWait;
            CurrentThread->GateObject = Gate;
            InsertTailList(&Gate->Header.WaitListHead, &WaitBlock->WaitListEntry);
            KiReleaseKobjectLock(Gate);
            KiSetContextSwapBusy(CurrentThread);
            KiReleaseThreadLock(CurrentThread);

            //
            // If the current thread is associated with a queue object, then
            // activate another thread if possible.
            //

            if (Queue != NULL) {
                if ((Queue = CurrentThread->Queue) != NULL) {
                    KiActivateWaiterQueue(Queue);
                }

                KiUnlockDispatcherDatabaseFromSynchLevel();
            }

            KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
            WaitStatus = (NTSTATUS)KiSwapThread(CurrentThread, KeGetCurrentPrcb());
            if (WaitStatus == STATUS_SUCCESS) {
                return;
            }
        }

    } while (TRUE);

    return;
}