示例#1
0
文件: apc.c 项目: 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);
        }
    }
}
示例#2
0
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;
    PRKWAIT_BLOCK NextBlock;
    PKMUTANT Mutant;
    PRKTHREAD Thread;
    PRKWAIT_BLOCK WaitBlock;
    PLIST_ENTRY WaitEntry;

    //
    // 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;
        if (WaitBlock->WaitType != WaitAny) {

            //
            // The wait type is wait all - if all the objects are in
            // a Signaled state, then satisfy the wait.
            //

            NextBlock = WaitBlock->NextWaitBlock;
            while (NextBlock != WaitBlock) {
                if (NextBlock->WaitKey != (CSHORT)(STATUS_TIMEOUT)) {
                    Mutant = (PKMUTANT)NextBlock->Object;
                    if ((Mutant->Header.Type == MutantObject) &&
                        (Mutant->Header.SignalState <= 0) &&
                        (Thread == Mutant->OwnerThread)) {
                        goto next;

                    } else if (Mutant->Header.SignalState <= 0) {
                        goto scan;
                    }
                }

            next:
                NextBlock = NextBlock->NextWaitBlock;
            }

            //
            // All objects associated with the wait are in the Signaled
            // state - satisfy the wait.
            //

            WaitEntry = WaitEntry->Blink;
            KiWaitSatisfyAll(WaitBlock);

        } else {

            //
            // The wait type is wait any - satisfy the wait.
            //

            WaitEntry = WaitEntry->Blink;
            KiWaitSatisfyAny((PKMUTANT)Event, Thread);
        }

        KiUnwaitThread(Thread, (NTSTATUS)WaitBlock->WaitKey, Increment);

    scan:
        WaitEntry = WaitEntry->Flink;
    }

    return;
}