[NTOS:MM] Quick fix: use SIZE_T instead of ULONG, because ULONG is 32-bit and on...
[reactos.git] / ntoskrnl / ke / thrdschd.c
1 /*
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)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 #ifdef _WIN64
16 # define InterlockedOrSetMember(Destination, SetMember) \
17 InterlockedOr64((PLONG64)Destination, SetMember);
18 #else
19 # define InterlockedOrSetMember(Destination, SetMember) \
20 InterlockedOr((PLONG)Destination, SetMember);
21 #endif
22
23 /* GLOBALS *******************************************************************/
24
25 ULONG_PTR KiIdleSummary;
26 ULONG_PTR KiIdleSMTSummary;
27
28 /* FUNCTIONS *****************************************************************/
29
30 PKTHREAD
31 FASTCALL
32 KiIdleSchedule(IN PKPRCB Prcb)
33 {
34 /* FIXME: TODO */
35 ASSERTMSG("SMP: Not yet implemented\n", FALSE);
36 return NULL;
37 }
38
39 VOID
40 FASTCALL
41 KiProcessDeferredReadyList(IN PKPRCB Prcb)
42 {
43 PSINGLE_LIST_ENTRY ListEntry;
44 PKTHREAD Thread;
45
46 /* Make sure there is something on the ready list */
47 ASSERT(Prcb->DeferredReadyListHead.Next != NULL);
48
49 /* Get the first entry and clear the list */
50 ListEntry = Prcb->DeferredReadyListHead.Next;
51 Prcb->DeferredReadyListHead.Next = NULL;
52
53 /* Start processing loop */
54 do
55 {
56 /* Get the thread and advance to the next entry */
57 Thread = CONTAINING_RECORD(ListEntry, KTHREAD, SwapListEntry);
58 ListEntry = ListEntry->Next;
59
60 /* Make the thread ready */
61 KiDeferredReadyThread(Thread);
62 } while (ListEntry != NULL);
63
64 /* Make sure the ready list is still empty */
65 ASSERT(Prcb->DeferredReadyListHead.Next == NULL);
66 }
67
68 VOID
69 FASTCALL
70 KiQueueReadyThread(IN PKTHREAD Thread,
71 IN PKPRCB Prcb)
72 {
73 /* Call the macro. We keep the API for compatibility with ASM code */
74 KxQueueReadyThread(Thread, Prcb);
75 }
76
77 VOID
78 FASTCALL
79 KiDeferredReadyThread(IN PKTHREAD Thread)
80 {
81 PKPRCB Prcb;
82 BOOLEAN Preempted;
83 ULONG Processor = 0;
84 KPRIORITY OldPriority;
85 PKTHREAD NextThread;
86
87 /* Sanity checks */
88 ASSERT(Thread->State == DeferredReady);
89 ASSERT((Thread->Priority >= 0) && (Thread->Priority <= HIGH_PRIORITY));
90
91 /* Check if we have any adjusts to do */
92 if (Thread->AdjustReason == AdjustBoost)
93 {
94 /* Lock the thread */
95 KiAcquireThreadLock(Thread);
96
97 /* Check if the priority is low enough to qualify for boosting */
98 if ((Thread->Priority <= Thread->AdjustIncrement) &&
99 (Thread->Priority < (LOW_REALTIME_PRIORITY - 3)) &&
100 !(Thread->DisableBoost))
101 {
102 /* Calculate the new priority based on the adjust increment */
103 OldPriority = min(Thread->AdjustIncrement + 1,
104 LOW_REALTIME_PRIORITY - 3);
105
106 /* Make sure we're not decreasing outside of the priority range */
107 ASSERT((Thread->PriorityDecrement >= 0) &&
108 (Thread->PriorityDecrement <= Thread->Priority));
109
110 /* Calculate the new priority decrement based on the boost */
111 Thread->PriorityDecrement += ((SCHAR)OldPriority - Thread->Priority);
112
113 /* Again verify that this decrement is valid */
114 ASSERT((Thread->PriorityDecrement >= 0) &&
115 (Thread->PriorityDecrement <= OldPriority));
116
117 /* Set the new priority */
118 Thread->Priority = (SCHAR)OldPriority;
119 }
120
121 /* We need 4 quanta, make sure we have them, then decrease by one */
122 if (Thread->Quantum < 4) Thread->Quantum = 4;
123 Thread->Quantum--;
124
125 /* Make sure the priority is still valid */
126 ASSERT((Thread->Priority >= 0) && (Thread->Priority <= HIGH_PRIORITY));
127
128 /* Release the lock and clear the adjust reason */
129 KiReleaseThreadLock(Thread);
130 Thread->AdjustReason = AdjustNone;
131 }
132 else if (Thread->AdjustReason == AdjustUnwait)
133 {
134 /* Acquire the thread lock and check if this is a real-time thread */
135 KiAcquireThreadLock(Thread);
136 if (Thread->Priority < LOW_REALTIME_PRIORITY)
137 {
138 /* It's not real time, but is it time critical? */
139 if (Thread->BasePriority >= (LOW_REALTIME_PRIORITY - 2))
140 {
141 /* It is, so simply reset its quantum */
142 Thread->Quantum = Thread->QuantumReset;
143 }
144 else
145 {
146 /* Has the priority been adjusted previously? */
147 if (!(Thread->PriorityDecrement) && (Thread->AdjustIncrement))
148 {
149 /* Yes, reset its quantum */
150 Thread->Quantum = Thread->QuantumReset;
151 }
152
153 /* Wait code already handles quantum adjustment during APCs */
154 if (Thread->WaitStatus != STATUS_KERNEL_APC)
155 {
156 /* Decrease the quantum by one and check if we're out */
157 if (--Thread->Quantum <= 0)
158 {
159 /* We are, reset the quantum and get a new priority */
160 Thread->Quantum = Thread->QuantumReset;
161 Thread->Priority = KiComputeNewPriority(Thread, 1);
162 }
163 }
164 }
165
166 /* Now check if we have no decrement and boosts are enabled */
167 if (!(Thread->PriorityDecrement) && !(Thread->DisableBoost))
168 {
169 /* Make sure we have an increment */
170 ASSERT(Thread->AdjustIncrement >= 0);
171
172 /* Calculate the new priority after the increment */
173 OldPriority = Thread->BasePriority + Thread->AdjustIncrement;
174
175 /* Check if this is a foreground process */
176 if (CONTAINING_RECORD(Thread->ApcState.Process, EPROCESS, Pcb)->
177 Vm.Flags.MemoryPriority == MEMORY_PRIORITY_FOREGROUND)
178 {
179 /* Apply the foreground boost */
180 OldPriority += PsPrioritySeparation;
181 }
182
183 /* Check if this new priority is higher */
184 if (OldPriority > Thread->Priority)
185 {
186 /* Make sure we don't go into the real time range */
187 if (OldPriority >= LOW_REALTIME_PRIORITY)
188 {
189 /* Normalize it back down one notch */
190 OldPriority = LOW_REALTIME_PRIORITY - 1;
191 }
192
193 /* Check if the priority is higher then the boosted base */
194 if (OldPriority > (Thread->BasePriority +
195 Thread->AdjustIncrement))
196 {
197 /* Setup a priority decrement to nullify the boost */
198 Thread->PriorityDecrement = ((SCHAR)OldPriority -
199 Thread->BasePriority -
200 Thread->AdjustIncrement);
201 }
202
203 /* Make sure that the priority decrement is valid */
204 ASSERT((Thread->PriorityDecrement >= 0) &&
205 (Thread->PriorityDecrement <= OldPriority));
206
207 /* Set this new priority */
208 Thread->Priority = (SCHAR)OldPriority;
209 }
210 }
211 }
212 else
213 {
214 /* It's a real-time thread, so just reset its quantum */
215 Thread->Quantum = Thread->QuantumReset;
216 }
217
218 /* Make sure the priority makes sense */
219 ASSERT((Thread->Priority >= 0) && (Thread->Priority <= HIGH_PRIORITY));
220
221 /* Release the thread lock and reset the adjust reason */
222 KiReleaseThreadLock(Thread);
223 Thread->AdjustReason = AdjustNone;
224 }
225
226 /* Clear thread preemption status and save current values */
227 Preempted = Thread->Preempted;
228 OldPriority = Thread->Priority;
229 Thread->Preempted = FALSE;
230
231 /* Queue the thread on CPU 0 and get the PRCB and lock it */
232 Thread->NextProcessor = 0;
233 Prcb = KiProcessorBlock[0];
234 KiAcquirePrcbLock(Prcb);
235
236 /* Check if we have an idle summary */
237 if (KiIdleSummary)
238 {
239 /* Clear it and set this thread as the next one */
240 KiIdleSummary = 0;
241 Thread->State = Standby;
242 Prcb->NextThread = Thread;
243
244 /* Unlock the PRCB and return */
245 KiReleasePrcbLock(Prcb);
246 return;
247 }
248
249 /* Set the CPU number */
250 Thread->NextProcessor = (UCHAR)Processor;
251
252 /* Get the next scheduled thread */
253 NextThread = Prcb->NextThread;
254 if (NextThread)
255 {
256 /* Sanity check */
257 ASSERT(NextThread->State == Standby);
258
259 /* Check if priority changed */
260 if (OldPriority > NextThread->Priority)
261 {
262 /* Preempt the thread */
263 NextThread->Preempted = TRUE;
264
265 /* Put this one as the next one */
266 Thread->State = Standby;
267 Prcb->NextThread = Thread;
268
269 /* Set it in deferred ready mode */
270 NextThread->State = DeferredReady;
271 NextThread->DeferredProcessor = Prcb->Number;
272 KiReleasePrcbLock(Prcb);
273 KiDeferredReadyThread(NextThread);
274 return;
275 }
276 }
277 else
278 {
279 /* Set the next thread as the current thread */
280 NextThread = Prcb->CurrentThread;
281 if (OldPriority > NextThread->Priority)
282 {
283 /* Preempt it if it's already running */
284 if (NextThread->State == Running) NextThread->Preempted = TRUE;
285
286 /* Set the thread on standby and as the next thread */
287 Thread->State = Standby;
288 Prcb->NextThread = Thread;
289
290 /* Release the lock */
291 KiReleasePrcbLock(Prcb);
292
293 /* Check if we're running on another CPU */
294 if (KeGetCurrentProcessorNumber() != Thread->NextProcessor)
295 {
296 /* We are, send an IPI */
297 KiIpiSend(AFFINITY_MASK(Thread->NextProcessor), IPI_DPC);
298 }
299 return;
300 }
301 }
302
303 /* Sanity check */
304 ASSERT((OldPriority >= 0) && (OldPriority <= HIGH_PRIORITY));
305
306 /* Set this thread as ready */
307 Thread->State = Ready;
308 Thread->WaitTime = KeTickCount.LowPart;
309
310 /* Insert this thread in the appropriate order */
311 Preempted ? InsertHeadList(&Prcb->DispatcherReadyListHead[OldPriority],
312 &Thread->WaitListEntry) :
313 InsertTailList(&Prcb->DispatcherReadyListHead[OldPriority],
314 &Thread->WaitListEntry);
315
316 /* Update the ready summary */
317 Prcb->ReadySummary |= PRIORITY_MASK(OldPriority);
318
319 /* Sanity check */
320 ASSERT(OldPriority == Thread->Priority);
321
322 /* Release the lock */
323 KiReleasePrcbLock(Prcb);
324 }
325
326 PKTHREAD
327 FASTCALL
328 KiSelectNextThread(IN PKPRCB Prcb)
329 {
330 PKTHREAD Thread;
331
332 /* Select a ready thread */
333 Thread = KiSelectReadyThread(0, Prcb);
334 if (!Thread)
335 {
336 /* Didn't find any, get the current idle thread */
337 Thread = Prcb->IdleThread;
338
339 /* Enable idle scheduling */
340 InterlockedOrSetMember(&KiIdleSummary, Prcb->SetMember);
341 Prcb->IdleSchedule = TRUE;
342
343 /* FIXME: SMT support */
344 ASSERTMSG("SMP: Not yet implemented\n", FALSE);
345 }
346
347 /* Sanity checks and return the thread */
348 ASSERT(Thread != NULL);
349 ASSERT((Thread->BasePriority == 0) || (Thread->Priority != 0));
350 return Thread;
351 }
352
353 LONG_PTR
354 FASTCALL
355 KiSwapThread(IN PKTHREAD CurrentThread,
356 IN PKPRCB Prcb)
357 {
358 BOOLEAN ApcState = FALSE;
359 KIRQL WaitIrql;
360 LONG_PTR WaitStatus;
361 PKTHREAD NextThread;
362 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
363
364 /* Acquire the PRCB lock */
365 KiAcquirePrcbLock(Prcb);
366
367 /* Get the next thread */
368 NextThread = Prcb->NextThread;
369 if (NextThread)
370 {
371 /* Already got a thread, set it up */
372 Prcb->NextThread = NULL;
373 Prcb->CurrentThread = NextThread;
374 NextThread->State = Running;
375 }
376 else
377 {
378 /* Try to find a ready thread */
379 NextThread = KiSelectReadyThread(0, Prcb);
380 if (NextThread)
381 {
382 /* Switch to it */
383 Prcb->CurrentThread = NextThread;
384 NextThread->State = Running;
385 }
386 else
387 {
388 /* Set the idle summary */
389 InterlockedOrSetMember(&KiIdleSummary, Prcb->SetMember);
390
391 /* Schedule the idle thread */
392 NextThread = Prcb->IdleThread;
393 Prcb->CurrentThread = NextThread;
394 NextThread->State = Running;
395 }
396 }
397
398 /* Sanity check and release the PRCB */
399 ASSERT(CurrentThread != Prcb->IdleThread);
400 KiReleasePrcbLock(Prcb);
401
402 /* Save the wait IRQL */
403 WaitIrql = CurrentThread->WaitIrql;
404
405 /* Swap contexts */
406 ApcState = KiSwapContext(WaitIrql, CurrentThread);
407
408 /* Get the wait status */
409 WaitStatus = CurrentThread->WaitStatus;
410
411 /* Check if we need to deliver APCs */
412 if (ApcState)
413 {
414 /* Lower to APC_LEVEL */
415 KeLowerIrql(APC_LEVEL);
416
417 /* Deliver APCs */
418 KiDeliverApc(KernelMode, NULL, NULL);
419 ASSERT(WaitIrql == 0);
420 }
421
422 /* Lower IRQL back to what it was and return the wait status */
423 KeLowerIrql(WaitIrql);
424 return WaitStatus;
425 }
426
427 VOID
428 NTAPI
429 KiReadyThread(IN PKTHREAD Thread)
430 {
431 IN PKPROCESS Process = Thread->ApcState.Process;
432
433 /* Check if the process is paged out */
434 if (Process->State != ProcessInMemory)
435 {
436 /* We don't page out processes in ROS */
437 ASSERT(FALSE);
438 }
439 else if (!Thread->KernelStackResident)
440 {
441 /* Increase the stack count */
442 ASSERT(Process->StackCount != MAXULONG_PTR);
443 Process->StackCount++;
444
445 /* Set the thread to transition */
446 ASSERT(Thread->State != Transition);
447 Thread->State = Transition;
448
449 /* The stack is always resident in ROS */
450 ASSERT(FALSE);
451 }
452 else
453 {
454 /* Insert the thread on the deferred ready list */
455 KiInsertDeferredReadyList(Thread);
456 }
457 }
458
459 VOID
460 NTAPI
461 KiAdjustQuantumThread(IN PKTHREAD Thread)
462 {
463 PKPRCB Prcb = KeGetCurrentPrcb();
464 PKTHREAD NextThread;
465
466 /* Acquire thread and PRCB lock */
467 KiAcquireThreadLock(Thread);
468 KiAcquirePrcbLock(Prcb);
469
470 /* Don't adjust for RT threads */
471 if ((Thread->Priority < LOW_REALTIME_PRIORITY) &&
472 (Thread->BasePriority < (LOW_REALTIME_PRIORITY - 2)))
473 {
474 /* Decrease Quantum by one and see if we've ran out */
475 if (--Thread->Quantum <= 0)
476 {
477 /* Return quantum */
478 Thread->Quantum = Thread->QuantumReset;
479
480 /* Calculate new Priority */
481 Thread->Priority = KiComputeNewPriority(Thread, 1);
482
483 /* Check if there's no next thread scheduled */
484 if (!Prcb->NextThread)
485 {
486 /* Select a ready thread and check if we found one */
487 NextThread = KiSelectReadyThread(Thread->Priority, Prcb);
488 if (NextThread)
489 {
490 /* Set it on standby and switch to it */
491 NextThread->State = Standby;
492 Prcb->NextThread = NextThread;
493 }
494 }
495 else
496 {
497 /* This thread can be preempted again */
498 Thread->Preempted = FALSE;
499 }
500 }
501 }
502
503 /* Release locks */
504 KiReleasePrcbLock(Prcb);
505 KiReleaseThreadLock(Thread);
506 KiExitDispatcher(Thread->WaitIrql);
507 }
508
509 VOID
510 FASTCALL
511 KiSetPriorityThread(IN PKTHREAD Thread,
512 IN KPRIORITY Priority)
513 {
514 PKPRCB Prcb;
515 ULONG Processor;
516 BOOLEAN RequestInterrupt = FALSE;
517 KPRIORITY OldPriority;
518 PKTHREAD NewThread;
519 ASSERT((Priority >= 0) && (Priority <= HIGH_PRIORITY));
520
521 /* Check if priority changed */
522 if (Thread->Priority != Priority)
523 {
524 /* Loop priority setting in case we need to start over */
525 for (;;)
526 {
527 /* Choose action based on thread's state */
528 if (Thread->State == Ready)
529 {
530 /* Make sure we're not on the ready queue */
531 if (!Thread->ProcessReadyQueue)
532 {
533 /* Get the PRCB for the thread and lock it */
534 Processor = Thread->NextProcessor;
535 Prcb = KiProcessorBlock[Processor];
536 KiAcquirePrcbLock(Prcb);
537
538 /* Make sure the thread is still ready and on this CPU */
539 if ((Thread->State == Ready) &&
540 (Thread->NextProcessor == Prcb->Number))
541 {
542 /* Sanity check */
543 ASSERT((Prcb->ReadySummary &
544 PRIORITY_MASK(Thread->Priority)));
545
546 /* Remove it from the current queue */
547 if (RemoveEntryList(&Thread->WaitListEntry))
548 {
549 /* Update the ready summary */
550 Prcb->ReadySummary ^= PRIORITY_MASK(Thread->
551 Priority);
552 }
553
554 /* Update priority */
555 Thread->Priority = (SCHAR)Priority;
556
557 /* Re-insert it at its current priority */
558 KiInsertDeferredReadyList(Thread);
559
560 /* Release the PRCB Lock */
561 KiReleasePrcbLock(Prcb);
562 }
563 else
564 {
565 /* Release the lock and loop again */
566 KiReleasePrcbLock(Prcb);
567 continue;
568 }
569 }
570 else
571 {
572 /* It's already on the ready queue, just update priority */
573 Thread->Priority = (SCHAR)Priority;
574 }
575 }
576 else if (Thread->State == Standby)
577 {
578 /* Get the PRCB for the thread and lock it */
579 Processor = Thread->NextProcessor;
580 Prcb = KiProcessorBlock[Processor];
581 KiAcquirePrcbLock(Prcb);
582
583 /* Check if we're still the next thread to run */
584 if (Thread == Prcb->NextThread)
585 {
586 /* Get the old priority and update ours */
587 OldPriority = Thread->Priority;
588 Thread->Priority = (SCHAR)Priority;
589
590 /* Check if there was a change */
591 if (Priority < OldPriority)
592 {
593 /* Find a new thread */
594 NewThread = KiSelectReadyThread(Priority + 1, Prcb);
595 if (NewThread)
596 {
597 /* Found a new one, set it on standby */
598 NewThread->State = Standby;
599 Prcb->NextThread = NewThread;
600
601 /* Dispatch our thread */
602 KiInsertDeferredReadyList(Thread);
603 }
604 }
605
606 /* Release the PRCB lock */
607 KiReleasePrcbLock(Prcb);
608 }
609 else
610 {
611 /* Release the lock and try again */
612 KiReleasePrcbLock(Prcb);
613 continue;
614 }
615 }
616 else if (Thread->State == Running)
617 {
618 /* Get the PRCB for the thread and lock it */
619 Processor = Thread->NextProcessor;
620 Prcb = KiProcessorBlock[Processor];
621 KiAcquirePrcbLock(Prcb);
622
623 /* Check if we're still the current thread running */
624 if (Thread == Prcb->CurrentThread)
625 {
626 /* Get the old priority and update ours */
627 OldPriority = Thread->Priority;
628 Thread->Priority = (SCHAR)Priority;
629
630 /* Check if there was a change and there's no new thread */
631 if ((Priority < OldPriority) && !(Prcb->NextThread))
632 {
633 /* Find a new thread */
634 NewThread = KiSelectReadyThread(Priority + 1, Prcb);
635 if (NewThread)
636 {
637 /* Found a new one, set it on standby */
638 NewThread->State = Standby;
639 Prcb->NextThread = NewThread;
640
641 /* Request an interrupt */
642 RequestInterrupt = TRUE;
643 }
644 }
645
646 /* Release the lock and check if we need an interrupt */
647 KiReleasePrcbLock(Prcb);
648 if (RequestInterrupt)
649 {
650 /* Check if we're running on another CPU */
651 if (KeGetCurrentProcessorNumber() != Processor)
652 {
653 /* We are, send an IPI */
654 KiIpiSend(AFFINITY_MASK(Processor), IPI_DPC);
655 }
656 }
657 }
658 else
659 {
660 /* Thread changed, release lock and restart */
661 KiReleasePrcbLock(Prcb);
662 continue;
663 }
664 }
665 else if (Thread->State == DeferredReady)
666 {
667 /* FIXME: TODO */
668 DPRINT1("Deferred state not yet supported\n");
669 ASSERT(FALSE);
670 }
671 else
672 {
673 /* Any other state, just change priority */
674 Thread->Priority = (SCHAR)Priority;
675 }
676
677 /* If we got here, then thread state was consistent, so bail out */
678 break;
679 }
680 }
681 }
682
683 KAFFINITY
684 FASTCALL
685 KiSetAffinityThread(IN PKTHREAD Thread,
686 IN KAFFINITY Affinity)
687 {
688 KAFFINITY OldAffinity;
689
690 /* Get the current affinity */
691 OldAffinity = Thread->UserAffinity;
692
693 /* Make sure that the affinity is valid */
694 if (((Affinity & Thread->ApcState.Process->Affinity) != (Affinity)) ||
695 (!Affinity))
696 {
697 /* Bugcheck the system */
698 KeBugCheck(INVALID_AFFINITY_SET);
699 }
700
701 /* Update the new affinity */
702 Thread->UserAffinity = Affinity;
703
704 /* Check if system affinity is disabled */
705 if (!Thread->SystemAffinityActive)
706 {
707 #ifdef CONFIG_SMP
708 /* FIXME: TODO */
709 DPRINT1("Affinity support disabled!\n");
710 #endif
711 }
712
713 /* Return the old affinity */
714 return OldAffinity;
715 }
716
717 //
718 // This macro exists because NtYieldExecution locklessly attempts to read from
719 // the KPRCB's ready summary, and the usual way of going through KeGetCurrentPrcb
720 // would require getting fs:1C first (or gs), and then doing another dereference.
721 // In an attempt to minimize the amount of instructions and potential race/tear
722 // that could happen, Windows seems to define this as a macro that directly acceses
723 // the ready summary through a single fs: read by going through the KPCR's PrcbData.
724 //
725 // See http://research.microsoft.com/en-us/collaboration/global/asia-pacific/
726 // programs/trk_case4_process-thread_management.pdf
727 //
728 // We need this per-arch because sometimes it's Prcb and sometimes PrcbData, and
729 // because on x86 it's FS, and on x64 it's GS (not sure what it is on ARM/PPC).
730 //
731 #ifdef _M_IX86
732 #define KiGetCurrentReadySummary() __readfsdword(FIELD_OFFSET(KIPCR, PrcbData.ReadySummary))
733 #elif _M_AMD64
734 #define KiGetCurrentReadySummary() __readgsdword(FIELD_OFFSET(KIPCR, Prcb.ReadySummary))
735 #else
736 #define KiGetCurrentReadySummary() KeGetCurrentPrcb()->ReadySummary
737 #endif
738
739 /*
740 * @implemented
741 */
742 NTSTATUS
743 NTAPI
744 NtYieldExecution(VOID)
745 {
746 NTSTATUS Status;
747 KIRQL OldIrql;
748 PKPRCB Prcb;
749 PKTHREAD Thread, NextThread;
750
751 /* NB: No instructions (other than entry code) should preceed this line */
752
753 /* Fail if there's no ready summary */
754 if (!KiGetCurrentReadySummary()) return STATUS_NO_YIELD_PERFORMED;
755
756 /* Now get the current thread, set the status... */
757 Status = STATUS_NO_YIELD_PERFORMED;
758 Thread = KeGetCurrentThread();
759
760 /* Raise IRQL to synch and get the KPRCB now */
761 OldIrql = KeRaiseIrqlToSynchLevel();
762 Prcb = KeGetCurrentPrcb();
763
764 /* Now check if there's still a ready summary */
765 if (Prcb->ReadySummary)
766 {
767 /* Acquire thread and PRCB lock */
768 KiAcquireThreadLock(Thread);
769 KiAcquirePrcbLock(Prcb);
770
771 /* Find a new thread to run if none was selected */
772 if (!Prcb->NextThread) Prcb->NextThread = KiSelectReadyThread(1, Prcb);
773
774 /* Make sure we still have a next thread to schedule */
775 NextThread = Prcb->NextThread;
776 if (NextThread)
777 {
778 /* Reset quantum and recalculate priority */
779 Thread->Quantum = Thread->QuantumReset;
780 Thread->Priority = KiComputeNewPriority(Thread, 1);
781
782 /* Release the thread lock */
783 KiReleaseThreadLock(Thread);
784
785 /* Set context swap busy */
786 KiSetThreadSwapBusy(Thread);
787
788 /* Set the new thread as running */
789 Prcb->NextThread = NULL;
790 Prcb->CurrentThread = NextThread;
791 NextThread->State = Running;
792
793 /* Setup a yield wait and queue the thread */
794 Thread->WaitReason = WrYieldExecution;
795 KxQueueReadyThread(Thread, Prcb);
796
797 /* Make it wait at APC_LEVEL */
798 Thread->WaitIrql = APC_LEVEL;
799
800 /* Sanity check */
801 ASSERT(OldIrql <= DISPATCH_LEVEL);
802
803 /* Swap to new thread */
804 KiSwapContext(APC_LEVEL, Thread);
805 Status = STATUS_SUCCESS;
806 }
807 else
808 {
809 /* Release the PRCB and thread lock */
810 KiReleasePrcbLock(Prcb);
811 KiReleaseThreadLock(Thread);
812 }
813 }
814
815 /* Lower IRQL and return */
816 KeLowerIrql(OldIrql);
817 return Status;
818 }