PKTHREAD FASTCALL KiSelectNextThread(IN PKPRCB Prcb) { PKTHREAD Thread; /* Select a ready thread */ Thread = KiSelectReadyThread(0, Prcb); if (!Thread) { /* Didn't find any, get the current idle thread */ Thread = Prcb->IdleThread; /* Enable idle scheduling */ InterlockedOrSetMember(&KiIdleSummary, Prcb->SetMember); Prcb->IdleSchedule = TRUE; /* FIXME: SMT support */ ASSERTMSG("SMP: Not yet implemented\n", FALSE); } /* Sanity checks and return the thread */ ASSERT(Thread != NULL); ASSERT((Thread->BasePriority == 0) || (Thread->Priority != 0)); return Thread; }
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); }
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); }
/* * @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; }
VOID FASTCALL KiSetPriorityThread(IN PKTHREAD Thread, IN KPRIORITY Priority) { PKPRCB Prcb; ULONG Processor; BOOLEAN RequestInterrupt = FALSE; KPRIORITY OldPriority; PKTHREAD NewThread; ASSERT((Priority >= 0) && (Priority <= HIGH_PRIORITY)); /* Check if priority changed */ if (Thread->Priority != Priority) { /* Loop priority setting in case we need to start over */ for (;;) { /* Choose action based on thread's state */ if (Thread->State == Ready) { /* Make sure we're not on the ready queue */ if (!Thread->ProcessReadyQueue) { /* Get the PRCB for the thread and lock it */ Processor = Thread->NextProcessor; Prcb = KiProcessorBlock[Processor]; KiAcquirePrcbLock(Prcb); /* Make sure the thread is still ready and on this CPU */ if ((Thread->State == Ready) && (Thread->NextProcessor == Prcb->Number)) { /* Sanity check */ ASSERT((Prcb->ReadySummary & PRIORITY_MASK(Thread->Priority))); /* Remove it from the current queue */ if (RemoveEntryList(&Thread->WaitListEntry)) { /* Update the ready summary */ Prcb->ReadySummary ^= PRIORITY_MASK(Thread-> Priority); } /* Update priority */ Thread->Priority = (SCHAR)Priority; /* Re-insert it at its current priority */ KiInsertDeferredReadyList(Thread); /* Release the PRCB Lock */ KiReleasePrcbLock(Prcb); } else { /* Release the lock and loop again */ KiReleasePrcbLock(Prcb); continue; } } else { /* It's already on the ready queue, just update priority */ Thread->Priority = (SCHAR)Priority; } } else if (Thread->State == Standby) { /* Get the PRCB for the thread and lock it */ Processor = Thread->NextProcessor; Prcb = KiProcessorBlock[Processor]; KiAcquirePrcbLock(Prcb); /* Check if we're still the next thread to run */ if (Thread == Prcb->NextThread) { /* Get the old priority and update ours */ OldPriority = Thread->Priority; Thread->Priority = (SCHAR)Priority; /* Check if there was a change */ if (Priority < OldPriority) { /* Find a new thread */ NewThread = KiSelectReadyThread(Priority + 1, Prcb); if (NewThread) { /* Found a new one, set it on standby */ NewThread->State = Standby; Prcb->NextThread = NewThread; /* Dispatch our thread */ KiInsertDeferredReadyList(Thread); } } /* Release the PRCB lock */ KiReleasePrcbLock(Prcb); } else { /* Release the lock and try again */ KiReleasePrcbLock(Prcb); continue; } } else if (Thread->State == Running) { /* Get the PRCB for the thread and lock it */ Processor = Thread->NextProcessor; Prcb = KiProcessorBlock[Processor]; KiAcquirePrcbLock(Prcb); /* Check if we're still the current thread running */ if (Thread == Prcb->CurrentThread) { /* Get the old priority and update ours */ OldPriority = Thread->Priority; Thread->Priority = (SCHAR)Priority; /* Check if there was a change and there's no new thread */ if ((Priority < OldPriority) && !(Prcb->NextThread)) { /* Find a new thread */ NewThread = KiSelectReadyThread(Priority + 1, Prcb); if (NewThread) { /* Found a new one, set it on standby */ NewThread->State = Standby; Prcb->NextThread = NewThread; /* Request an interrupt */ RequestInterrupt = TRUE; } } /* Release the lock and check if we need an interrupt */ KiReleasePrcbLock(Prcb); if (RequestInterrupt) { /* Check if we're running on another CPU */ if (KeGetCurrentProcessorNumber() != Processor) { /* We are, send an IPI */ KiIpiSend(AFFINITY_MASK(Processor), IPI_DPC); } } } else { /* Thread changed, release lock and restart */ KiReleasePrcbLock(Prcb); continue; } } else if (Thread->State == DeferredReady) { /* FIXME: TODO */ DPRINT1("Deferred state not yet supported\n"); ASSERT(FALSE); } else { /* Any other state, just change priority */ Thread->Priority = (SCHAR)Priority; } /* If we got here, then thread state was consistent, so bail out */ break; } } }
LONG_PTR FASTCALL KiSwapThread(IN PKTHREAD CurrentThread, IN PKPRCB Prcb) { BOOLEAN ApcState = FALSE; KIRQL WaitIrql; LONG_PTR WaitStatus; PKTHREAD NextThread; ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL); /* Acquire the PRCB lock */ KiAcquirePrcbLock(Prcb); /* Get the next thread */ NextThread = Prcb->NextThread; if (NextThread) { /* Already got a thread, set it up */ Prcb->NextThread = NULL; Prcb->CurrentThread = NextThread; NextThread->State = Running; } else { /* Try to find a ready thread */ NextThread = KiSelectReadyThread(0, Prcb); if (NextThread) { /* Switch to it */ Prcb->CurrentThread = NextThread; NextThread->State = Running; } else { /* Set the idle summary */ InterlockedOrSetMember(&KiIdleSummary, Prcb->SetMember); /* Schedule the idle thread */ NextThread = Prcb->IdleThread; Prcb->CurrentThread = NextThread; NextThread->State = Running; } } /* Sanity check and release the PRCB */ ASSERT(CurrentThread != Prcb->IdleThread); KiReleasePrcbLock(Prcb); /* Save the wait IRQL */ WaitIrql = CurrentThread->WaitIrql; /* Swap contexts */ ApcState = KiSwapContext(WaitIrql, CurrentThread); /* Get the wait status */ WaitStatus = CurrentThread->WaitStatus; /* Check if we need to deliver APCs */ if (ApcState) { /* Lower to APC_LEVEL */ KeLowerIrql(APC_LEVEL); /* Deliver APCs */ KiDeliverApc(KernelMode, NULL, NULL); ASSERT(WaitIrql == 0); } /* Lower IRQL back to what it was and return the wait status */ KeLowerIrql(WaitIrql); return WaitStatus; }
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; } }