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 KAFFINITY KiIdleSummary; 26 KAFFINITY 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 } 819