2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/thrdschd.c
5 * PURPOSE: Kernel Thread Scheduler (Affinity, Priority, Scheduling)
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
15 /* GLOBALS *******************************************************************/
18 ULONG KiIdleSMTSummary
;
20 /* FUNCTIONS *****************************************************************/
24 KiIdleSchedule(IN PKPRCB Prcb
)
27 ASSERTMSG("SMP: Not yet implemented\n", FALSE
);
33 KiProcessDeferredReadyList(IN PKPRCB Prcb
)
35 PSINGLE_LIST_ENTRY ListEntry
;
38 /* Make sure there is something on the ready list */
39 ASSERT(Prcb
->DeferredReadyListHead
.Next
!= NULL
);
41 /* Get the first entry and clear the list */
42 ListEntry
= Prcb
->DeferredReadyListHead
.Next
;
43 Prcb
->DeferredReadyListHead
.Next
= NULL
;
45 /* Start processing loop */
48 /* Get the thread and advance to the next entry */
49 Thread
= CONTAINING_RECORD(ListEntry
, KTHREAD
, SwapListEntry
);
50 ListEntry
= ListEntry
->Next
;
52 /* Make the thread ready */
53 KiDeferredReadyThread(Thread
);
54 } while (ListEntry
!= NULL
);
56 /* Make sure the ready list is still empty */
57 ASSERT(Prcb
->DeferredReadyListHead
.Next
== NULL
);
62 KiQueueReadyThread(IN PKTHREAD Thread
,
65 /* Call the macro. We keep the API for compatibility with ASM code */
66 KxQueueReadyThread(Thread
, Prcb
);
71 KiDeferredReadyThread(IN PKTHREAD Thread
)
76 KPRIORITY OldPriority
;
80 ASSERT(Thread
->State
== DeferredReady
);
81 ASSERT((Thread
->Priority
>= 0) && (Thread
->Priority
<= HIGH_PRIORITY
));
83 /* Check if we have any adjusts to do */
84 if (Thread
->AdjustReason
== AdjustBoost
)
87 KiAcquireThreadLock(Thread
);
89 /* Check if the priority is low enough to qualify for boosting */
90 if ((Thread
->Priority
<= Thread
->AdjustIncrement
) &&
91 (Thread
->Priority
< (LOW_REALTIME_PRIORITY
- 3)) &&
92 !(Thread
->DisableBoost
))
94 /* Calculate the new priority based on the adjust increment */
95 OldPriority
= min(Thread
->AdjustIncrement
+ 1,
96 LOW_REALTIME_PRIORITY
- 3);
98 /* Make sure we're not decreasing outside of the priority range */
99 ASSERT((Thread
->PriorityDecrement
>= 0) &&
100 (Thread
->PriorityDecrement
<= Thread
->Priority
));
102 /* Calculate the new priority decrement based on the boost */
103 Thread
->PriorityDecrement
+= ((SCHAR
)OldPriority
- Thread
->Priority
);
105 /* Again verify that this decrement is valid */
106 ASSERT((Thread
->PriorityDecrement
>= 0) &&
107 (Thread
->PriorityDecrement
<= OldPriority
));
109 /* Set the new priority */
110 Thread
->Priority
= (SCHAR
)OldPriority
;
113 /* We need 4 quanta, make sure we have them, then decrease by one */
114 if (Thread
->Quantum
< 4) Thread
->Quantum
= 4;
117 /* Make sure the priority is still valid */
118 ASSERT((Thread
->Priority
>= 0) && (Thread
->Priority
<= HIGH_PRIORITY
));
120 /* Release the lock and clear the adjust reason */
121 KiReleaseThreadLock(Thread
);
122 Thread
->AdjustReason
= AdjustNone
;
124 else if (Thread
->AdjustReason
== AdjustUnwait
)
126 /* Acquire the thread lock and check if this is a real-time thread */
127 KiAcquireThreadLock(Thread
);
128 if (Thread
->Priority
< LOW_REALTIME_PRIORITY
)
130 /* It's not real time, but is it time critical? */
131 if (Thread
->BasePriority
>= (LOW_REALTIME_PRIORITY
- 2))
133 /* It is, so simply reset its quantum */
134 Thread
->Quantum
= Thread
->QuantumReset
;
138 /* Has the priority been adjusted previously? */
139 if (!(Thread
->PriorityDecrement
) && (Thread
->AdjustIncrement
))
141 /* Yes, reset its quantum */
142 Thread
->Quantum
= Thread
->QuantumReset
;
145 /* Wait code already handles quantum adjustment during APCs */
146 if (Thread
->WaitStatus
!= STATUS_KERNEL_APC
)
148 /* Decrease the quantum by one and check if we're out */
149 if (--Thread
->Quantum
<= 0)
151 /* We are, reset the quantum and get a new priority */
152 Thread
->Quantum
= Thread
->QuantumReset
;
153 Thread
->Priority
= KiComputeNewPriority(Thread
, 1);
158 /* Now check if we have no decrement and boosts are enabled */
159 if (!(Thread
->PriorityDecrement
) && !(Thread
->DisableBoost
))
161 /* Make sure we have an increment */
162 ASSERT(Thread
->AdjustIncrement
>= 0);
164 /* Calculate the new priority after the increment */
165 OldPriority
= Thread
->BasePriority
+ Thread
->AdjustIncrement
;
167 /* Check if this new priority is higher */
168 if (OldPriority
> Thread
->Priority
)
170 /* Make sure we don't go into the real time range */
171 if (OldPriority
>= LOW_REALTIME_PRIORITY
)
173 /* Normalize it back down one notch */
174 OldPriority
= LOW_REALTIME_PRIORITY
- 1;
177 /* Check if the priority is higher then the boosted base */
178 if (OldPriority
> (Thread
->BasePriority
+
179 Thread
->AdjustIncrement
))
181 /* Setup a priority decrement to nullify the boost */
182 Thread
->PriorityDecrement
= ((SCHAR
)OldPriority
-
183 Thread
->BasePriority
-
184 Thread
->AdjustIncrement
);
187 /* Make sure that the priority decrement is valid */
188 ASSERT((Thread
->PriorityDecrement
>= 0) &&
189 (Thread
->PriorityDecrement
<= OldPriority
));
191 /* Set this new priority */
192 Thread
->Priority
= (SCHAR
)OldPriority
;
198 /* It's a real-time thread, so just reset its quantum */
199 Thread
->Quantum
= Thread
->QuantumReset
;
202 /* Make sure the priority makes sense */
203 ASSERT((Thread
->Priority
>= 0) && (Thread
->Priority
<= HIGH_PRIORITY
));
205 /* Release the thread lock and reset the adjust reason */
206 KiReleaseThreadLock(Thread
);
207 Thread
->AdjustReason
= AdjustNone
;
210 /* Clear thread preemption status and save current values */
211 Preempted
= Thread
->Preempted
;
212 OldPriority
= Thread
->Priority
;
213 Thread
->Preempted
= FALSE
;
215 /* Queue the thread on CPU 0 and get the PRCB and lock it */
216 Thread
->NextProcessor
= 0;
217 Prcb
= KiProcessorBlock
[0];
218 KiAcquirePrcbLock(Prcb
);
220 /* Check if we have an idle summary */
223 /* Clear it and set this thread as the next one */
225 Thread
->State
= Standby
;
226 Prcb
->NextThread
= Thread
;
228 /* Unlock the PRCB and return */
229 KiReleasePrcbLock(Prcb
);
233 /* Set the CPU number */
234 Thread
->NextProcessor
= (UCHAR
)Processor
;
236 /* Get the next scheduled thread */
237 NextThread
= Prcb
->NextThread
;
241 ASSERT(NextThread
->State
== Standby
);
243 /* Check if priority changed */
244 if (OldPriority
> NextThread
->Priority
)
246 /* Preempt the thread */
247 NextThread
->Preempted
= TRUE
;
249 /* Put this one as the next one */
250 Thread
->State
= Standby
;
251 Prcb
->NextThread
= Thread
;
253 /* Set it in deferred ready mode */
254 NextThread
->State
= DeferredReady
;
255 NextThread
->DeferredProcessor
= Prcb
->Number
;
256 KiReleasePrcbLock(Prcb
);
257 KiDeferredReadyThread(NextThread
);
263 /* Set the next thread as the current thread */
264 NextThread
= Prcb
->CurrentThread
;
265 if (OldPriority
> NextThread
->Priority
)
267 /* Preempt it if it's already running */
268 if (NextThread
->State
== Running
) NextThread
->Preempted
= TRUE
;
270 /* Set the thread on standby and as the next thread */
271 Thread
->State
= Standby
;
272 Prcb
->NextThread
= Thread
;
274 /* Release the lock */
275 KiReleasePrcbLock(Prcb
);
277 /* Check if we're running on another CPU */
278 if (KeGetCurrentProcessorNumber() != Thread
->NextProcessor
)
280 /* We are, send an IPI */
281 KiIpiSend(AFFINITY_MASK(Thread
->NextProcessor
), IPI_DPC
);
288 ASSERT((OldPriority
>= 0) && (OldPriority
<= HIGH_PRIORITY
));
290 /* Set this thread as ready */
291 Thread
->State
= Ready
;
292 Thread
->WaitTime
= KeTickCount
.LowPart
;
294 /* Insert this thread in the appropriate order */
295 Preempted
? InsertHeadList(&Prcb
->DispatcherReadyListHead
[OldPriority
],
296 &Thread
->WaitListEntry
) :
297 InsertTailList(&Prcb
->DispatcherReadyListHead
[OldPriority
],
298 &Thread
->WaitListEntry
);
300 /* Update the ready summary */
301 Prcb
->ReadySummary
|= PRIORITY_MASK(OldPriority
);
304 ASSERT(OldPriority
== Thread
->Priority
);
306 /* Release the lock */
307 KiReleasePrcbLock(Prcb
);
312 KiSelectNextThread(IN PKPRCB Prcb
)
316 /* Select a ready thread */
317 Thread
= KiSelectReadyThread(0, Prcb
);
320 /* Didn't find any, get the current idle thread */
321 Thread
= Prcb
->IdleThread
;
323 /* Enable idle scheduling */
324 InterlockedOr((PLONG
) &KiIdleSummary
, Prcb
->SetMember
);
325 Prcb
->IdleSchedule
= TRUE
;
327 /* FIXME: SMT support */
328 ASSERTMSG("SMP: Not yet implemented\n", FALSE
);
331 /* Sanity checks and return the thread */
332 ASSERT(Thread
!= NULL
);
333 ASSERT((Thread
->BasePriority
== 0) || (Thread
->Priority
!= 0));
339 KiSwapThread(IN PKTHREAD CurrentThread
,
342 BOOLEAN ApcState
= FALSE
;
346 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL
);
348 /* Acquire the PRCB lock */
349 KiAcquirePrcbLock(Prcb
);
351 /* Get the next thread */
352 NextThread
= Prcb
->NextThread
;
355 /* Already got a thread, set it up */
356 Prcb
->NextThread
= NULL
;
357 Prcb
->CurrentThread
= NextThread
;
358 NextThread
->State
= Running
;
362 /* Try to find a ready thread */
363 NextThread
= KiSelectReadyThread(0, Prcb
);
367 Prcb
->CurrentThread
= NextThread
;
368 NextThread
->State
= Running
;
372 /* Set the idle summary */
373 InterlockedOr((PLONG
)&KiIdleSummary
, Prcb
->SetMember
);
375 /* Schedule the idle thread */
376 NextThread
= Prcb
->IdleThread
;
377 Prcb
->CurrentThread
= NextThread
;
378 NextThread
->State
= Running
;
382 /* Sanity check and release the PRCB */
383 ASSERT(CurrentThread
!= Prcb
->IdleThread
);
384 KiReleasePrcbLock(Prcb
);
386 /* Save the wait IRQL */
387 WaitIrql
= CurrentThread
->WaitIrql
;
390 ApcState
= KiSwapContext(WaitIrql
, CurrentThread
);
392 /* Get the wait status */
393 WaitStatus
= CurrentThread
->WaitStatus
;
395 /* Check if we need to deliver APCs */
398 /* Lower to APC_LEVEL */
399 KeLowerIrql(APC_LEVEL
);
402 KiDeliverApc(KernelMode
, NULL
, NULL
);
403 ASSERT(WaitIrql
== 0);
406 /* Lower IRQL back to what it was and return the wait status */
407 KeLowerIrql(WaitIrql
);
413 KiReadyThread(IN PKTHREAD Thread
)
415 IN PKPROCESS Process
= Thread
->ApcState
.Process
;
417 /* Check if the process is paged out */
418 if (Process
->State
!= ProcessInMemory
)
420 /* We don't page out processes in ROS */
423 else if (!Thread
->KernelStackResident
)
425 /* Increase the stack count */
426 ASSERT(Process
->StackCount
!= MAXULONG_PTR
);
427 Process
->StackCount
++;
429 /* Set the thread to transition */
430 ASSERT(Thread
->State
!= Transition
);
431 Thread
->State
= Transition
;
433 /* The stack is always resident in ROS */
438 /* Insert the thread on the deferred ready list */
439 KiInsertDeferredReadyList(Thread
);
445 KiAdjustQuantumThread(IN PKTHREAD Thread
)
447 PKPRCB Prcb
= KeGetCurrentPrcb();
450 /* Acquire thread and PRCB lock */
451 KiAcquireThreadLock(Thread
);
452 KiAcquirePrcbLock(Prcb
);
454 /* Don't adjust for RT threads */
455 if ((Thread
->Priority
< LOW_REALTIME_PRIORITY
) &&
456 (Thread
->BasePriority
< (LOW_REALTIME_PRIORITY
- 2)))
458 /* Decrease Quantum by one and see if we've ran out */
459 if (--Thread
->Quantum
<= 0)
462 Thread
->Quantum
= Thread
->QuantumReset
;
464 /* Calculate new Priority */
465 Thread
->Priority
= KiComputeNewPriority(Thread
, 1);
467 /* Check if there's no next thread scheduled */
468 if (!Prcb
->NextThread
)
470 /* Select a ready thread and check if we found one */
471 NextThread
= KiSelectReadyThread(Thread
->Priority
, Prcb
);
474 /* Set it on standby and switch to it */
475 NextThread
->State
= Standby
;
476 Prcb
->NextThread
= NextThread
;
481 /* This thread can be preempted again */
482 Thread
->Preempted
= FALSE
;
488 KiReleasePrcbLock(Prcb
);
489 KiReleaseThreadLock(Thread
);
490 KiExitDispatcher(Thread
->WaitIrql
);
495 KiSetPriorityThread(IN PKTHREAD Thread
,
496 IN KPRIORITY Priority
)
500 BOOLEAN RequestInterrupt
= FALSE
;
501 KPRIORITY OldPriority
;
503 ASSERT((Priority
>= 0) && (Priority
<= HIGH_PRIORITY
));
505 /* Check if priority changed */
506 if (Thread
->Priority
!= Priority
)
508 /* Loop priority setting in case we need to start over */
511 /* Choose action based on thread's state */
512 if (Thread
->State
== Ready
)
514 /* Make sure we're not on the ready queue */
515 if (!Thread
->ProcessReadyQueue
)
517 /* Get the PRCB for the thread and lock it */
518 Processor
= Thread
->NextProcessor
;
519 Prcb
= KiProcessorBlock
[Processor
];
520 KiAcquirePrcbLock(Prcb
);
522 /* Make sure the thread is still ready and on this CPU */
523 if ((Thread
->State
== Ready
) &&
524 (Thread
->NextProcessor
== Prcb
->Number
))
527 ASSERT((Prcb
->ReadySummary
&
528 PRIORITY_MASK(Thread
->Priority
)));
530 /* Remove it from the current queue */
531 if (RemoveEntryList(&Thread
->WaitListEntry
))
533 /* Update the ready summary */
534 Prcb
->ReadySummary
^= PRIORITY_MASK(Thread
->
538 /* Update priority */
539 Thread
->Priority
= (SCHAR
)Priority
;
541 /* Re-insert it at its current priority */
542 KiInsertDeferredReadyList(Thread
);
544 /* Release the PRCB Lock */
545 KiReleasePrcbLock(Prcb
);
549 /* Release the lock and loop again */
550 KiReleasePrcbLock(Prcb
);
556 /* It's already on the ready queue, just update priority */
557 Thread
->Priority
= (SCHAR
)Priority
;
560 else if (Thread
->State
== Standby
)
562 /* Get the PRCB for the thread and lock it */
563 Processor
= Thread
->NextProcessor
;
564 Prcb
= KiProcessorBlock
[Processor
];
565 KiAcquirePrcbLock(Prcb
);
567 /* Check if we're still the next thread to run */
568 if (Thread
== Prcb
->NextThread
)
570 /* Get the old priority and update ours */
571 OldPriority
= Thread
->Priority
;
572 Thread
->Priority
= (SCHAR
)Priority
;
574 /* Check if there was a change */
575 if (Priority
< OldPriority
)
577 /* Find a new thread */
578 NewThread
= KiSelectReadyThread(Priority
+ 1, Prcb
);
581 /* Found a new one, set it on standby */
582 NewThread
->State
= Standby
;
583 Prcb
->NextThread
= NewThread
;
585 /* Dispatch our thread */
586 KiInsertDeferredReadyList(Thread
);
590 /* Release the PRCB lock */
591 KiReleasePrcbLock(Prcb
);
595 /* Release the lock and try again */
596 KiReleasePrcbLock(Prcb
);
600 else if (Thread
->State
== Running
)
602 /* Get the PRCB for the thread and lock it */
603 Processor
= Thread
->NextProcessor
;
604 Prcb
= KiProcessorBlock
[Processor
];
605 KiAcquirePrcbLock(Prcb
);
607 /* Check if we're still the current thread running */
608 if (Thread
== Prcb
->CurrentThread
)
610 /* Get the old priority and update ours */
611 OldPriority
= Thread
->Priority
;
612 Thread
->Priority
= (SCHAR
)Priority
;
614 /* Check if there was a change and there's no new thread */
615 if ((Priority
< OldPriority
) && !(Prcb
->NextThread
))
617 /* Find a new thread */
618 NewThread
= KiSelectReadyThread(Priority
+ 1, Prcb
);
621 /* Found a new one, set it on standby */
622 NewThread
->State
= Standby
;
623 Prcb
->NextThread
= NewThread
;
625 /* Request an interrupt */
626 RequestInterrupt
= TRUE
;
630 /* Release the lock and check if we need an interrupt */
631 KiReleasePrcbLock(Prcb
);
632 if (RequestInterrupt
)
634 /* Check if we're running on another CPU */
635 if (KeGetCurrentProcessorNumber() != Processor
)
637 /* We are, send an IPI */
638 KiIpiSend(AFFINITY_MASK(Processor
), IPI_DPC
);
644 /* Thread changed, release lock and restart */
645 KiReleasePrcbLock(Prcb
);
649 else if (Thread
->State
== DeferredReady
)
652 DPRINT1("Deferred state not yet supported\n");
657 /* Any other state, just change priority */
658 Thread
->Priority
= (SCHAR
)Priority
;
661 /* If we got here, then thread state was consistent, so bail out */
669 KiSetAffinityThread(IN PKTHREAD Thread
,
670 IN KAFFINITY Affinity
)
672 KAFFINITY OldAffinity
;
674 /* Get the current affinity */
675 OldAffinity
= Thread
->UserAffinity
;
677 /* Make sure that the affinity is valid */
678 if (((Affinity
& Thread
->ApcState
.Process
->Affinity
) != (Affinity
)) ||
681 /* Bugcheck the system */
682 KeBugCheck(INVALID_AFFINITY_SET
);
685 /* Update the new affinity */
686 Thread
->UserAffinity
= Affinity
;
688 /* Check if system affinity is disabled */
689 if (!Thread
->SystemAffinityActive
)
692 DPRINT1("Affinity support disabled!\n");
695 /* Return the old affinity */
704 NtYieldExecution(VOID
)
706 NTSTATUS Status
= STATUS_NO_YIELD_PERFORMED
;
708 PKPRCB Prcb
= KeGetCurrentPrcb();
709 PKTHREAD Thread
= KeGetCurrentThread(), NextThread
;
711 /* Fail if there's no ready summary */
712 if (!Prcb
->ReadySummary
) return Status
;
714 /* Raise IRQL to synch */
715 OldIrql
= KeRaiseIrqlToSynchLevel();
717 /* Now check if there's still a ready summary */
718 if (Prcb
->ReadySummary
)
720 /* Acquire thread and PRCB lock */
721 KiAcquireThreadLock(Thread
);
722 KiAcquirePrcbLock(Prcb
);
724 /* Find a new thread to run if none was selected */
725 if (!Prcb
->NextThread
) Prcb
->NextThread
= KiSelectReadyThread(1, Prcb
);
727 /* Make sure we still have a next thread to schedule */
728 NextThread
= Prcb
->NextThread
;
731 /* Reset quantum and recalculate priority */
732 Thread
->Quantum
= Thread
->QuantumReset
;
733 Thread
->Priority
= KiComputeNewPriority(Thread
, 1);
735 /* Release the thread lock */
736 KiReleaseThreadLock(Thread
);
738 /* Set context swap busy */
739 KiSetThreadSwapBusy(Thread
);
741 /* Set the new thread as running */
742 Prcb
->NextThread
= NULL
;
743 Prcb
->CurrentThread
= NextThread
;
744 NextThread
->State
= Running
;
746 /* Setup a yield wait and queue the thread */
747 Thread
->WaitReason
= WrYieldExecution
;
748 KxQueueReadyThread(Thread
, Prcb
);
750 /* Make it wait at APC_LEVEL */
751 Thread
->WaitIrql
= APC_LEVEL
;
754 ASSERT(OldIrql
<= DISPATCH_LEVEL
);
756 /* Swap to new thread */
757 KiSwapContext(APC_LEVEL
, Thread
);
758 Status
= STATUS_SUCCESS
;
762 /* Release the PRCB and thread lock */
763 KiReleasePrcbLock(Prcb
);
764 KiReleaseThreadLock(Thread
);
768 /* Lower IRQL and return */
769 KeLowerIrql(OldIrql
);