* Sync up to trunk head (r64894).
[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 new priority is higher */
176 if (OldPriority > Thread->Priority)
177 {
178 /* Make sure we don't go into the real time range */
179 if (OldPriority >= LOW_REALTIME_PRIORITY)
180 {
181 /* Normalize it back down one notch */
182 OldPriority = LOW_REALTIME_PRIORITY - 1;
183 }
184
185 /* Check if the priority is higher then the boosted base */
186 if (OldPriority > (Thread->BasePriority +
187 Thread->AdjustIncrement))
188 {
189 /* Setup a priority decrement to nullify the boost */
190 Thread->PriorityDecrement = ((SCHAR)OldPriority -
191 Thread->BasePriority -
192 Thread->AdjustIncrement);
193 }
194
195 /* Make sure that the priority decrement is valid */
196 ASSERT((Thread->PriorityDecrement >= 0) &&
197 (Thread->PriorityDecrement <= OldPriority));
198
199 /* Set this new priority */
200 Thread->Priority = (SCHAR)OldPriority;
201 }
202 }
203 }
204 else
205 {
206 /* It's a real-time thread, so just reset its quantum */
207 Thread->Quantum = Thread->QuantumReset;
208 }
209
210 /* Make sure the priority makes sense */
211 ASSERT((Thread->Priority >= 0) && (Thread->Priority <= HIGH_PRIORITY));
212
213 /* Release the thread lock and reset the adjust reason */
214 KiReleaseThreadLock(Thread);
215 Thread->AdjustReason = AdjustNone;
216 }
217
218 /* Clear thread preemption status and save current values */
219 Preempted = Thread->Preempted;
220 OldPriority = Thread->Priority;
221 Thread->Preempted = FALSE;
222
223 /* Queue the thread on CPU 0 and get the PRCB and lock it */
224 Thread->NextProcessor = 0;
225 Prcb = KiProcessorBlock[0];
226 KiAcquirePrcbLock(Prcb);
227
228 /* Check if we have an idle summary */
229 if (KiIdleSummary)
230 {
231 /* Clear it and set this thread as the next one */
232 KiIdleSummary = 0;
233 Thread->State = Standby;
234 Prcb->NextThread = Thread;
235
236 /* Unlock the PRCB and return */
237 KiReleasePrcbLock(Prcb);
238 return;
239 }
240
241 /* Set the CPU number */
242 Thread->NextProcessor = (UCHAR)Processor;
243
244 /* Get the next scheduled thread */
245 NextThread = Prcb->NextThread;
246 if (NextThread)
247 {
248 /* Sanity check */
249 ASSERT(NextThread->State == Standby);
250
251 /* Check if priority changed */
252 if (OldPriority > NextThread->Priority)
253 {
254 /* Preempt the thread */
255 NextThread->Preempted = TRUE;
256
257 /* Put this one as the next one */
258 Thread->State = Standby;
259 Prcb->NextThread = Thread;
260
261 /* Set it in deferred ready mode */
262 NextThread->State = DeferredReady;
263 NextThread->DeferredProcessor = Prcb->Number;
264 KiReleasePrcbLock(Prcb);
265 KiDeferredReadyThread(NextThread);
266 return;
267 }
268 }
269 else
270 {
271 /* Set the next thread as the current thread */
272 NextThread = Prcb->CurrentThread;
273 if (OldPriority > NextThread->Priority)
274 {
275 /* Preempt it if it's already running */
276 if (NextThread->State == Running) NextThread->Preempted = TRUE;
277
278 /* Set the thread on standby and as the next thread */
279 Thread->State = Standby;
280 Prcb->NextThread = Thread;
281
282 /* Release the lock */
283 KiReleasePrcbLock(Prcb);
284
285 /* Check if we're running on another CPU */
286 if (KeGetCurrentProcessorNumber() != Thread->NextProcessor)
287 {
288 /* We are, send an IPI */
289 KiIpiSend(AFFINITY_MASK(Thread->NextProcessor), IPI_DPC);
290 }
291 return;
292 }
293 }
294
295 /* Sanity check */
296 ASSERT((OldPriority >= 0) && (OldPriority <= HIGH_PRIORITY));
297
298 /* Set this thread as ready */
299 Thread->State = Ready;
300 Thread->WaitTime = KeTickCount.LowPart;
301
302 /* Insert this thread in the appropriate order */
303 Preempted ? InsertHeadList(&Prcb->DispatcherReadyListHead[OldPriority],
304 &Thread->WaitListEntry) :
305 InsertTailList(&Prcb->DispatcherReadyListHead[OldPriority],
306 &Thread->WaitListEntry);
307
308 /* Update the ready summary */
309 Prcb->ReadySummary |= PRIORITY_MASK(OldPriority);
310
311 /* Sanity check */
312 ASSERT(OldPriority == Thread->Priority);
313
314 /* Release the lock */
315 KiReleasePrcbLock(Prcb);
316 }
317
318 PKTHREAD
319 FASTCALL
320 KiSelectNextThread(IN PKPRCB Prcb)
321 {
322 PKTHREAD Thread;
323
324 /* Select a ready thread */
325 Thread = KiSelectReadyThread(0, Prcb);
326 if (!Thread)
327 {
328 /* Didn't find any, get the current idle thread */
329 Thread = Prcb->IdleThread;
330
331 /* Enable idle scheduling */
332 InterlockedOrSetMember(&KiIdleSummary, Prcb->SetMember);
333 Prcb->IdleSchedule = TRUE;
334
335 /* FIXME: SMT support */
336 ASSERTMSG("SMP: Not yet implemented\n", FALSE);
337 }
338
339 /* Sanity checks and return the thread */
340 ASSERT(Thread != NULL);
341 ASSERT((Thread->BasePriority == 0) || (Thread->Priority != 0));
342 return Thread;
343 }
344
345 LONG_PTR
346 FASTCALL
347 KiSwapThread(IN PKTHREAD CurrentThread,
348 IN PKPRCB Prcb)
349 {
350 BOOLEAN ApcState = FALSE;
351 KIRQL WaitIrql;
352 LONG_PTR WaitStatus;
353 PKTHREAD NextThread;
354 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
355
356 /* Acquire the PRCB lock */
357 KiAcquirePrcbLock(Prcb);
358
359 /* Get the next thread */
360 NextThread = Prcb->NextThread;
361 if (NextThread)
362 {
363 /* Already got a thread, set it up */
364 Prcb->NextThread = NULL;
365 Prcb->CurrentThread = NextThread;
366 NextThread->State = Running;
367 }
368 else
369 {
370 /* Try to find a ready thread */
371 NextThread = KiSelectReadyThread(0, Prcb);
372 if (NextThread)
373 {
374 /* Switch to it */
375 Prcb->CurrentThread = NextThread;
376 NextThread->State = Running;
377 }
378 else
379 {
380 /* Set the idle summary */
381 InterlockedOrSetMember(&KiIdleSummary, Prcb->SetMember);
382
383 /* Schedule the idle thread */
384 NextThread = Prcb->IdleThread;
385 Prcb->CurrentThread = NextThread;
386 NextThread->State = Running;
387 }
388 }
389
390 /* Sanity check and release the PRCB */
391 ASSERT(CurrentThread != Prcb->IdleThread);
392 KiReleasePrcbLock(Prcb);
393
394 /* Save the wait IRQL */
395 WaitIrql = CurrentThread->WaitIrql;
396
397 /* Swap contexts */
398 ApcState = KiSwapContext(WaitIrql, CurrentThread);
399
400 /* Get the wait status */
401 WaitStatus = CurrentThread->WaitStatus;
402
403 /* Check if we need to deliver APCs */
404 if (ApcState)
405 {
406 /* Lower to APC_LEVEL */
407 KeLowerIrql(APC_LEVEL);
408
409 /* Deliver APCs */
410 KiDeliverApc(KernelMode, NULL, NULL);
411 ASSERT(WaitIrql == 0);
412 }
413
414 /* Lower IRQL back to what it was and return the wait status */
415 KeLowerIrql(WaitIrql);
416 return WaitStatus;
417 }
418
419 VOID
420 NTAPI
421 KiReadyThread(IN PKTHREAD Thread)
422 {
423 IN PKPROCESS Process = Thread->ApcState.Process;
424
425 /* Check if the process is paged out */
426 if (Process->State != ProcessInMemory)
427 {
428 /* We don't page out processes in ROS */
429 ASSERT(FALSE);
430 }
431 else if (!Thread->KernelStackResident)
432 {
433 /* Increase the stack count */
434 ASSERT(Process->StackCount != MAXULONG_PTR);
435 Process->StackCount++;
436
437 /* Set the thread to transition */
438 ASSERT(Thread->State != Transition);
439 Thread->State = Transition;
440
441 /* The stack is always resident in ROS */
442 ASSERT(FALSE);
443 }
444 else
445 {
446 /* Insert the thread on the deferred ready list */
447 KiInsertDeferredReadyList(Thread);
448 }
449 }
450
451 VOID
452 NTAPI
453 KiAdjustQuantumThread(IN PKTHREAD Thread)
454 {
455 PKPRCB Prcb = KeGetCurrentPrcb();
456 PKTHREAD NextThread;
457
458 /* Acquire thread and PRCB lock */
459 KiAcquireThreadLock(Thread);
460 KiAcquirePrcbLock(Prcb);
461
462 /* Don't adjust for RT threads */
463 if ((Thread->Priority < LOW_REALTIME_PRIORITY) &&
464 (Thread->BasePriority < (LOW_REALTIME_PRIORITY - 2)))
465 {
466 /* Decrease Quantum by one and see if we've ran out */
467 if (--Thread->Quantum <= 0)
468 {
469 /* Return quantum */
470 Thread->Quantum = Thread->QuantumReset;
471
472 /* Calculate new Priority */
473 Thread->Priority = KiComputeNewPriority(Thread, 1);
474
475 /* Check if there's no next thread scheduled */
476 if (!Prcb->NextThread)
477 {
478 /* Select a ready thread and check if we found one */
479 NextThread = KiSelectReadyThread(Thread->Priority, Prcb);
480 if (NextThread)
481 {
482 /* Set it on standby and switch to it */
483 NextThread->State = Standby;
484 Prcb->NextThread = NextThread;
485 }
486 }
487 else
488 {
489 /* This thread can be preempted again */
490 Thread->Preempted = FALSE;
491 }
492 }
493 }
494
495 /* Release locks */
496 KiReleasePrcbLock(Prcb);
497 KiReleaseThreadLock(Thread);
498 KiExitDispatcher(Thread->WaitIrql);
499 }
500
501 VOID
502 FASTCALL
503 KiSetPriorityThread(IN PKTHREAD Thread,
504 IN KPRIORITY Priority)
505 {
506 PKPRCB Prcb;
507 ULONG Processor;
508 BOOLEAN RequestInterrupt = FALSE;
509 KPRIORITY OldPriority;
510 PKTHREAD NewThread;
511 ASSERT((Priority >= 0) && (Priority <= HIGH_PRIORITY));
512
513 /* Check if priority changed */
514 if (Thread->Priority != Priority)
515 {
516 /* Loop priority setting in case we need to start over */
517 for (;;)
518 {
519 /* Choose action based on thread's state */
520 if (Thread->State == Ready)
521 {
522 /* Make sure we're not on the ready queue */
523 if (!Thread->ProcessReadyQueue)
524 {
525 /* Get the PRCB for the thread and lock it */
526 Processor = Thread->NextProcessor;
527 Prcb = KiProcessorBlock[Processor];
528 KiAcquirePrcbLock(Prcb);
529
530 /* Make sure the thread is still ready and on this CPU */
531 if ((Thread->State == Ready) &&
532 (Thread->NextProcessor == Prcb->Number))
533 {
534 /* Sanity check */
535 ASSERT((Prcb->ReadySummary &
536 PRIORITY_MASK(Thread->Priority)));
537
538 /* Remove it from the current queue */
539 if (RemoveEntryList(&Thread->WaitListEntry))
540 {
541 /* Update the ready summary */
542 Prcb->ReadySummary ^= PRIORITY_MASK(Thread->
543 Priority);
544 }
545
546 /* Update priority */
547 Thread->Priority = (SCHAR)Priority;
548
549 /* Re-insert it at its current priority */
550 KiInsertDeferredReadyList(Thread);
551
552 /* Release the PRCB Lock */
553 KiReleasePrcbLock(Prcb);
554 }
555 else
556 {
557 /* Release the lock and loop again */
558 KiReleasePrcbLock(Prcb);
559 continue;
560 }
561 }
562 else
563 {
564 /* It's already on the ready queue, just update priority */
565 Thread->Priority = (SCHAR)Priority;
566 }
567 }
568 else if (Thread->State == Standby)
569 {
570 /* Get the PRCB for the thread and lock it */
571 Processor = Thread->NextProcessor;
572 Prcb = KiProcessorBlock[Processor];
573 KiAcquirePrcbLock(Prcb);
574
575 /* Check if we're still the next thread to run */
576 if (Thread == Prcb->NextThread)
577 {
578 /* Get the old priority and update ours */
579 OldPriority = Thread->Priority;
580 Thread->Priority = (SCHAR)Priority;
581
582 /* Check if there was a change */
583 if (Priority < OldPriority)
584 {
585 /* Find a new thread */
586 NewThread = KiSelectReadyThread(Priority + 1, Prcb);
587 if (NewThread)
588 {
589 /* Found a new one, set it on standby */
590 NewThread->State = Standby;
591 Prcb->NextThread = NewThread;
592
593 /* Dispatch our thread */
594 KiInsertDeferredReadyList(Thread);
595 }
596 }
597
598 /* Release the PRCB lock */
599 KiReleasePrcbLock(Prcb);
600 }
601 else
602 {
603 /* Release the lock and try again */
604 KiReleasePrcbLock(Prcb);
605 continue;
606 }
607 }
608 else if (Thread->State == Running)
609 {
610 /* Get the PRCB for the thread and lock it */
611 Processor = Thread->NextProcessor;
612 Prcb = KiProcessorBlock[Processor];
613 KiAcquirePrcbLock(Prcb);
614
615 /* Check if we're still the current thread running */
616 if (Thread == Prcb->CurrentThread)
617 {
618 /* Get the old priority and update ours */
619 OldPriority = Thread->Priority;
620 Thread->Priority = (SCHAR)Priority;
621
622 /* Check if there was a change and there's no new thread */
623 if ((Priority < OldPriority) && !(Prcb->NextThread))
624 {
625 /* Find a new thread */
626 NewThread = KiSelectReadyThread(Priority + 1, Prcb);
627 if (NewThread)
628 {
629 /* Found a new one, set it on standby */
630 NewThread->State = Standby;
631 Prcb->NextThread = NewThread;
632
633 /* Request an interrupt */
634 RequestInterrupt = TRUE;
635 }
636 }
637
638 /* Release the lock and check if we need an interrupt */
639 KiReleasePrcbLock(Prcb);
640 if (RequestInterrupt)
641 {
642 /* Check if we're running on another CPU */
643 if (KeGetCurrentProcessorNumber() != Processor)
644 {
645 /* We are, send an IPI */
646 KiIpiSend(AFFINITY_MASK(Processor), IPI_DPC);
647 }
648 }
649 }
650 else
651 {
652 /* Thread changed, release lock and restart */
653 KiReleasePrcbLock(Prcb);
654 continue;
655 }
656 }
657 else if (Thread->State == DeferredReady)
658 {
659 /* FIXME: TODO */
660 DPRINT1("Deferred state not yet supported\n");
661 ASSERT(FALSE);
662 }
663 else
664 {
665 /* Any other state, just change priority */
666 Thread->Priority = (SCHAR)Priority;
667 }
668
669 /* If we got here, then thread state was consistent, so bail out */
670 break;
671 }
672 }
673 }
674
675 KAFFINITY
676 FASTCALL
677 KiSetAffinityThread(IN PKTHREAD Thread,
678 IN KAFFINITY Affinity)
679 {
680 KAFFINITY OldAffinity;
681
682 /* Get the current affinity */
683 OldAffinity = Thread->UserAffinity;
684
685 /* Make sure that the affinity is valid */
686 if (((Affinity & Thread->ApcState.Process->Affinity) != (Affinity)) ||
687 (!Affinity))
688 {
689 /* Bugcheck the system */
690 KeBugCheck(INVALID_AFFINITY_SET);
691 }
692
693 /* Update the new affinity */
694 Thread->UserAffinity = Affinity;
695
696 /* Check if system affinity is disabled */
697 if (!Thread->SystemAffinityActive)
698 {
699 /* FIXME: TODO */
700 DPRINT1("Affinity support disabled!\n");
701 }
702
703 /* Return the old affinity */
704 return OldAffinity;
705 }
706
707 //
708 // This macro exists because NtYieldExecution locklessly attempts to read from
709 // the KPRCB's ready summary, and the usual way of going through KeGetCurrentPrcb
710 // would require getting fs:1C first (or gs), and then doing another dereference.
711 // In an attempt to minimize the amount of instructions and potential race/tear
712 // that could happen, Windows seems to define this as a macro that directly acceses
713 // the ready summary through a single fs: read by going through the KPCR's PrcbData.
714 //
715 // See http://research.microsoft.com/en-us/collaboration/global/asia-pacific/
716 // programs/trk_case4_process-thread_management.pdf
717 //
718 // We need this per-arch because sometimes it's Prcb and sometimes PrcbData, and
719 // because on x86 it's FS, and on x64 it's GS (not sure what it is on ARM/PPC).
720 //
721 #ifdef _M_IX86
722 #define KiGetCurrentReadySummary() __readfsdword(FIELD_OFFSET(KIPCR, PrcbData.ReadySummary))
723 #elif _M_AMD64
724 #define KiGetCurrentReadySummary() __readgsdword(FIELD_OFFSET(KIPCR, Prcb.ReadySummary))
725 #else
726 #error Implement me!
727 #endif
728
729 /*
730 * @implemented
731 */
732 NTSTATUS
733 NTAPI
734 NtYieldExecution(VOID)
735 {
736 NTSTATUS Status;
737 KIRQL OldIrql;
738 PKPRCB Prcb;
739 PKTHREAD Thread, NextThread;
740
741 /* NB: No instructions (other than entry code) should preceed this line */
742
743 /* Fail if there's no ready summary */
744 if (!KiGetCurrentReadySummary()) return STATUS_NO_YIELD_PERFORMED;
745
746 /* Now get the current thread, set the status... */
747 Status = STATUS_NO_YIELD_PERFORMED;
748 Thread = KeGetCurrentThread();
749
750 /* Raise IRQL to synch and get the KPRCB now */
751 OldIrql = KeRaiseIrqlToSynchLevel();
752 Prcb = KeGetCurrentPrcb();
753
754 /* Now check if there's still a ready summary */
755 if (Prcb->ReadySummary)
756 {
757 /* Acquire thread and PRCB lock */
758 KiAcquireThreadLock(Thread);
759 KiAcquirePrcbLock(Prcb);
760
761 /* Find a new thread to run if none was selected */
762 if (!Prcb->NextThread) Prcb->NextThread = KiSelectReadyThread(1, Prcb);
763
764 /* Make sure we still have a next thread to schedule */
765 NextThread = Prcb->NextThread;
766 if (NextThread)
767 {
768 /* Reset quantum and recalculate priority */
769 Thread->Quantum = Thread->QuantumReset;
770 Thread->Priority = KiComputeNewPriority(Thread, 1);
771
772 /* Release the thread lock */
773 KiReleaseThreadLock(Thread);
774
775 /* Set context swap busy */
776 KiSetThreadSwapBusy(Thread);
777
778 /* Set the new thread as running */
779 Prcb->NextThread = NULL;
780 Prcb->CurrentThread = NextThread;
781 NextThread->State = Running;
782
783 /* Setup a yield wait and queue the thread */
784 Thread->WaitReason = WrYieldExecution;
785 KxQueueReadyThread(Thread, Prcb);
786
787 /* Make it wait at APC_LEVEL */
788 Thread->WaitIrql = APC_LEVEL;
789
790 /* Sanity check */
791 ASSERT(OldIrql <= DISPATCH_LEVEL);
792
793 /* Swap to new thread */
794 KiSwapContext(APC_LEVEL, Thread);
795 Status = STATUS_SUCCESS;
796 }
797 else
798 {
799 /* Release the PRCB and thread lock */
800 KiReleasePrcbLock(Prcb);
801 KiReleaseThreadLock(Thread);
802 }
803 }
804
805 /* Lower IRQL and return */
806 KeLowerIrql(OldIrql);
807 return Status;
808 }