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 *******************************************************************/
17 LIST_ENTRY PriorityListHead
[MAXIMUM_PRIORITY
];
18 ULONG PriorityListMask
= 0;
20 ULONG KiIdleSMTSummary
;
22 /* FUNCTIONS *****************************************************************/
26 KiRequestReschedule(CCHAR Processor
)
30 Pcr
= (PKPCR
)(KPCR_BASE
+ Processor
* PAGE_SIZE
);
31 Pcr
->Prcb
->QuantumEnd
= TRUE
;
32 KiIpiSendRequest(1 << Processor
, IPI_DPC
);
37 KiInsertIntoThreadList(KPRIORITY Priority
,
40 ASSERT(Ready
== Thread
->State
);
41 ASSERT(Thread
->Priority
== Priority
);
43 if (Priority
>= MAXIMUM_PRIORITY
|| Priority
< LOW_PRIORITY
) {
45 DPRINT1("Invalid thread priority (%d)\n", Priority
);
49 InsertTailList(&PriorityListHead
[Priority
], &Thread
->WaitListEntry
);
50 PriorityListMask
|= (1 << Priority
);
55 KiRemoveFromThreadList(PKTHREAD Thread
)
57 ASSERT(Ready
== Thread
->State
);
58 RemoveEntryList(&Thread
->WaitListEntry
);
59 if (IsListEmpty(&PriorityListHead
[(ULONG
)Thread
->Priority
])) {
61 PriorityListMask
&= ~(1 << Thread
->Priority
);
67 KiScanThreadList(KPRIORITY Priority
,
73 Mask
= (1 << Priority
);
75 if (PriorityListMask
& Mask
) {
77 LIST_FOR_EACH(current
, &PriorityListHead
[Priority
], KTHREAD
, WaitListEntry
) {
79 if (current
->State
!= Ready
) {
81 DPRINT1("%p/%d\n", current
, current
->State
);
84 ASSERT(current
->State
== Ready
);
86 if (current
->Affinity
& Affinity
) {
88 KiRemoveFromThreadList(current
);
99 KiDispatchThreadNoLock(ULONG NewThreadStatus
)
101 KPRIORITY CurrentPriority
;
104 PKTHREAD CurrentThread
= KeGetCurrentThread();
107 DPRINT("KiDispatchThreadNoLock() %d/%d/%d/%d\n", KeGetCurrentProcessorNumber(),
108 CurrentThread
, NewThreadStatus
, CurrentThread
->State
);
110 CurrentThread
->State
= (UCHAR
)NewThreadStatus
;
112 if (NewThreadStatus
== Ready
) {
114 KiInsertIntoThreadList(CurrentThread
->Priority
,
118 Affinity
= 1 << KeGetCurrentProcessorNumber();
120 for (CurrentPriority
= HIGH_PRIORITY
; CurrentPriority
>= LOW_PRIORITY
; CurrentPriority
--) {
122 Candidate
= KiScanThreadList(CurrentPriority
, Affinity
);
124 if (Candidate
== CurrentThread
) {
126 Candidate
->State
= Running
;
127 KiReleaseDispatcherLockFromDpcLevel();
131 if (Candidate
!= NULL
) {
136 DPRINT("Scheduling %x(%d)\n",Candidate
, CurrentPriority
);
138 Candidate
->State
= Running
;
140 OldThread
= CurrentThread
;
141 CurrentThread
= Candidate
;
142 IdleThread
= KeGetCurrentPrcb()->IdleThread
;
144 if (OldThread
== IdleThread
) {
146 KiIdleSummary
&= ~Affinity
;
148 } else if (CurrentThread
== IdleThread
) {
150 KiIdleSummary
|= Affinity
;
153 MmUpdatePageDir((PEPROCESS
)PsGetCurrentProcess(),((PETHREAD
)CurrentThread
)->ThreadsProcess
, sizeof(EPROCESS
));
155 /* Special note for Filip: This will release the Dispatcher DB Lock ;-) -- Alex */
156 DPRINT("You are : %x, swapping to: %x.\n", OldThread
, CurrentThread
);
157 KeGetCurrentPrcb()->CurrentThread
= CurrentThread
;
158 ApcState
= KiSwapContext(OldThread
, CurrentThread
);
159 DPRINT("You are : %x, swapped from: %x\n", OldThread
, CurrentThread
);
164 DPRINT1("CRITICAL: No threads are ready (CPU%d)\n", KeGetCurrentProcessorNumber());
171 KiSwapThread(IN PKTHREAD CurrentThread
,
175 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL
);
177 /* Find a new thread to run */
178 ApcState
= KiDispatchThreadNoLock(Waiting
);
180 /* Check if we need to deliver APCs */
183 /* Lower to APC_LEVEL */
184 KeLowerIrql(APC_LEVEL
);
187 KiDeliverApc(KernelMode
, NULL
, NULL
);
188 ASSERT(CurrentThread
->WaitIrql
== 0);
191 /* Lower IRQL back to what it was */
192 KfLowerIrql(CurrentThread
->WaitIrql
);
194 /* Return the wait status */
195 return CurrentThread
->WaitStatus
;
200 KiDispatchThread(ULONG NewThreadStatus
)
204 if (KeGetCurrentPrcb()->IdleThread
== NULL
) {
208 OldIrql
= KiAcquireDispatcherLock();
209 KiDispatchThreadNoLock(NewThreadStatus
);
210 KeLowerIrql(OldIrql
);
215 KiReadyThread(IN PKTHREAD Thread
)
217 IN PKPROCESS Process
= Thread
->ApcState
.Process
;
219 /* Check if the process is paged out */
220 if (Process
->State
!= ProcessInMemory
)
222 /* We don't page out processes in ROS */
225 else if (!Thread
->KernelStackResident
)
227 /* Increase the stack count */
228 ASSERT(Process
->StackCount
!= MAXULONG_PTR
);
229 Process
->StackCount
++;
231 /* Set the thread to transition */
232 ASSERT(Thread
->State
!= Transition
);
233 Thread
->State
= Transition
;
235 /* The stack is always resident in ROS */
240 /* Insert the thread on the deferred ready list */
242 KiInsertDeferredReadyList(Thread
);
244 /* Insert the thread into the thread list */
245 Thread
->State
= Ready
;
246 KiInsertIntoThreadList(Thread
->Priority
, Thread
);
253 KiAdjustQuantumThread(IN PKTHREAD Thread
)
257 /* Don't adjust for RT threads */
258 if ((Thread
->Priority
< LOW_REALTIME_PRIORITY
) &&
259 Thread
->BasePriority
< LOW_REALTIME_PRIORITY
- 2)
261 /* Decrease Quantum by one and see if we've ran out */
262 if (--Thread
->Quantum
<= 0)
265 Thread
->Quantum
= Thread
->QuantumReset
;
267 /* Calculate new Priority */
268 Priority
= Thread
->Priority
- (Thread
->PriorityDecrement
+ 1);
270 /* Normalize it if we've gone too low */
271 if (Priority
< Thread
->BasePriority
) Priority
= Thread
->BasePriority
;
273 /* Reset the priority decrement, we've done it */
274 Thread
->PriorityDecrement
= 0;
276 /* Set the new priority, if needed */
277 if (Priority
!= Thread
->Priority
)
280 * FIXME: This should be a call to KiSetPriorityThread but
281 * due to the current ""scheduler"" in ROS, it can't be done
282 * cleanly since it actualyl dispatches threads instead.
284 Thread
->Priority
= (SCHAR
)Priority
;
288 /* FIXME: Priority hasn't changed, find a new thread */
293 /* Nothing to do... */
299 KiSetPriorityThread(PKTHREAD Thread
,
303 KPRIORITY OldPriority
= Thread
->Priority
;
307 DPRINT("Changing prio to : %lx\n", Priority
);
309 /* Check if priority changed */
310 if (OldPriority
!= Priority
)
313 Thread
->Priority
= (SCHAR
)Priority
;
315 /* Choose action based on thread's state */
316 if (Thread
->State
== Ready
)
318 /* Remove it from the current queue */
319 KiRemoveFromThreadList(Thread
);
321 /* Re-insert it at its current priority */
322 KiInsertIntoThreadList(Priority
, Thread
);
324 /* Check if the old priority was lower */
325 if (KeGetCurrentThread()->Priority
< Priority
)
327 /* Dispatch it immediately */
328 KiDispatchThreadNoLock(Ready
);
333 else if (Thread
->State
== Running
)
335 /* Check if the new priority is lower */
336 if (Priority
< OldPriority
)
338 /* Check for threads with a higher priority */
339 Mask
= ~((1 << (Priority
+ 1)) - 1);
340 if (PriorityListMask
& Mask
)
342 /* Found a thread, is it us? */
343 if (Thread
== KeGetCurrentThread())
346 KiDispatchThreadNoLock(Ready
);
353 for (i
= 0; i
< KeNumberProcessors
; i
++)
355 /* Get the PCR for this CPU */
356 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
358 /* Reschedule if the new one is already on a CPU */
359 if (Pcr
->Prcb
->CurrentThread
== Thread
)
361 KiReleaseDispatcherLockFromDpcLevel();
362 KiRequestReschedule(i
);
373 /* Return to caller */
380 KiSetAffinityThread(IN PKTHREAD Thread
,
381 IN KAFFINITY Affinity
,
384 KAFFINITY OldAffinity
;
389 /* Make sure that the affinity is valid */
390 if (((Affinity
& Thread
->ApcState
.Process
->Affinity
) != (Affinity
)) ||
393 /* Bugcheck the system */
394 KeBugCheck(INVALID_AFFINITY_SET
);
397 /* Get the old affinity */
398 OldAffinity
= Thread
->UserAffinity
;
400 Thread
->UserAffinity
= Affinity
;
402 if (Thread
->SystemAffinityActive
== FALSE
) {
404 Thread
->Affinity
= Affinity
;
406 if (Thread
->State
== Running
) {
408 ProcessorMask
= 1 << KeGetCurrentProcessorNumber();
409 if (Thread
== KeGetCurrentThread()) {
411 if (!(Affinity
& ProcessorMask
)) {
413 KiDispatchThreadNoLock(Ready
);
420 for (i
= 0; i
< KeNumberProcessors
; i
++) {
422 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
423 if (Pcr
->Prcb
->CurrentThread
== Thread
) {
425 if (!(Affinity
& ProcessorMask
)) {
427 KiReleaseDispatcherLockFromDpcLevel();
428 KiRequestReschedule(i
);
437 ASSERT (i
< KeNumberProcessors
);
451 NtYieldExecution(VOID
)
454 // TODO (nothing too hard, just want to test out other code)
456 //DPRINT1("NO YIELD PERFORMED! If you see this, contact Alex\n");
457 //return STATUS_NO_YIELD_PERFORMED;
458 KiDispatchThread(Ready
);
459 return STATUS_SUCCESS
;