/*++ * @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); } } }
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; }