Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenthrdschd.c
Go to the documentation of this file.
00001 /* 00002 * PROJECT: ReactOS Kernel 00003 * LICENSE: GPL - See COPYING in the top level directory 00004 * FILE: ntoskrnl/ke/thrdschd.c 00005 * PURPOSE: Kernel Thread Scheduler (Affinity, Priority, Scheduling) 00006 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 00007 */ 00008 00009 /* INCLUDES ******************************************************************/ 00010 00011 #include <ntoskrnl.h> 00012 #define NDEBUG 00013 #include <debug.h> 00014 00015 #ifdef _WIN64 00016 # define InterlockedOrSetMember(Destination, SetMember) \ 00017 InterlockedOr64((PLONG64)Destination, SetMember); 00018 #else 00019 # define InterlockedOrSetMember(Destination, SetMember) \ 00020 InterlockedOr((PLONG)Destination, SetMember); 00021 #endif 00022 00023 /* GLOBALS *******************************************************************/ 00024 00025 ULONG_PTR KiIdleSummary; 00026 ULONG_PTR KiIdleSMTSummary; 00027 00028 /* FUNCTIONS *****************************************************************/ 00029 00030 PKTHREAD 00031 FASTCALL 00032 KiIdleSchedule(IN PKPRCB Prcb) 00033 { 00034 /* FIXME: TODO */ 00035 ASSERTMSG("SMP: Not yet implemented\n", FALSE); 00036 return NULL; 00037 } 00038 00039 VOID 00040 FASTCALL 00041 KiProcessDeferredReadyList(IN PKPRCB Prcb) 00042 { 00043 PSINGLE_LIST_ENTRY ListEntry; 00044 PKTHREAD Thread; 00045 00046 /* Make sure there is something on the ready list */ 00047 ASSERT(Prcb->DeferredReadyListHead.Next != NULL); 00048 00049 /* Get the first entry and clear the list */ 00050 ListEntry = Prcb->DeferredReadyListHead.Next; 00051 Prcb->DeferredReadyListHead.Next = NULL; 00052 00053 /* Start processing loop */ 00054 do 00055 { 00056 /* Get the thread and advance to the next entry */ 00057 Thread = CONTAINING_RECORD(ListEntry, KTHREAD, SwapListEntry); 00058 ListEntry = ListEntry->Next; 00059 00060 /* Make the thread ready */ 00061 KiDeferredReadyThread(Thread); 00062 } while (ListEntry != NULL); 00063 00064 /* Make sure the ready list is still empty */ 00065 ASSERT(Prcb->DeferredReadyListHead.Next == NULL); 00066 } 00067 00068 VOID 00069 FASTCALL 00070 KiQueueReadyThread(IN PKTHREAD Thread, 00071 IN PKPRCB Prcb) 00072 { 00073 /* Call the macro. We keep the API for compatibility with ASM code */ 00074 KxQueueReadyThread(Thread, Prcb); 00075 } 00076 00077 VOID 00078 FASTCALL 00079 KiDeferredReadyThread(IN PKTHREAD Thread) 00080 { 00081 PKPRCB Prcb; 00082 BOOLEAN Preempted; 00083 ULONG Processor = 0; 00084 KPRIORITY OldPriority; 00085 PKTHREAD NextThread; 00086 00087 /* Sanity checks */ 00088 ASSERT(Thread->State == DeferredReady); 00089 ASSERT((Thread->Priority >= 0) && (Thread->Priority <= HIGH_PRIORITY)); 00090 00091 /* Check if we have any adjusts to do */ 00092 if (Thread->AdjustReason == AdjustBoost) 00093 { 00094 /* Lock the thread */ 00095 KiAcquireThreadLock(Thread); 00096 00097 /* Check if the priority is low enough to qualify for boosting */ 00098 if ((Thread->Priority <= Thread->AdjustIncrement) && 00099 (Thread->Priority < (LOW_REALTIME_PRIORITY - 3)) && 00100 !(Thread->DisableBoost)) 00101 { 00102 /* Calculate the new priority based on the adjust increment */ 00103 OldPriority = min(Thread->AdjustIncrement + 1, 00104 LOW_REALTIME_PRIORITY - 3); 00105 00106 /* Make sure we're not decreasing outside of the priority range */ 00107 ASSERT((Thread->PriorityDecrement >= 0) && 00108 (Thread->PriorityDecrement <= Thread->Priority)); 00109 00110 /* Calculate the new priority decrement based on the boost */ 00111 Thread->PriorityDecrement += ((SCHAR)OldPriority - Thread->Priority); 00112 00113 /* Again verify that this decrement is valid */ 00114 ASSERT((Thread->PriorityDecrement >= 0) && 00115 (Thread->PriorityDecrement <= OldPriority)); 00116 00117 /* Set the new priority */ 00118 Thread->Priority = (SCHAR)OldPriority; 00119 } 00120 00121 /* We need 4 quanta, make sure we have them, then decrease by one */ 00122 if (Thread->Quantum < 4) Thread->Quantum = 4; 00123 Thread->Quantum--; 00124 00125 /* Make sure the priority is still valid */ 00126 ASSERT((Thread->Priority >= 0) && (Thread->Priority <= HIGH_PRIORITY)); 00127 00128 /* Release the lock and clear the adjust reason */ 00129 KiReleaseThreadLock(Thread); 00130 Thread->AdjustReason = AdjustNone; 00131 } 00132 else if (Thread->AdjustReason == AdjustUnwait) 00133 { 00134 /* Acquire the thread lock and check if this is a real-time thread */ 00135 KiAcquireThreadLock(Thread); 00136 if (Thread->Priority < LOW_REALTIME_PRIORITY) 00137 { 00138 /* It's not real time, but is it time critical? */ 00139 if (Thread->BasePriority >= (LOW_REALTIME_PRIORITY - 2)) 00140 { 00141 /* It is, so simply reset its quantum */ 00142 Thread->Quantum = Thread->QuantumReset; 00143 } 00144 else 00145 { 00146 /* Has the priority been adjusted previously? */ 00147 if (!(Thread->PriorityDecrement) && (Thread->AdjustIncrement)) 00148 { 00149 /* Yes, reset its quantum */ 00150 Thread->Quantum = Thread->QuantumReset; 00151 } 00152 00153 /* Wait code already handles quantum adjustment during APCs */ 00154 if (Thread->WaitStatus != STATUS_KERNEL_APC) 00155 { 00156 /* Decrease the quantum by one and check if we're out */ 00157 if (--Thread->Quantum <= 0) 00158 { 00159 /* We are, reset the quantum and get a new priority */ 00160 Thread->Quantum = Thread->QuantumReset; 00161 Thread->Priority = KiComputeNewPriority(Thread, 1); 00162 } 00163 } 00164 } 00165 00166 /* Now check if we have no decrement and boosts are enabled */ 00167 if (!(Thread->PriorityDecrement) && !(Thread->DisableBoost)) 00168 { 00169 /* Make sure we have an increment */ 00170 ASSERT(Thread->AdjustIncrement >= 0); 00171 00172 /* Calculate the new priority after the increment */ 00173 OldPriority = Thread->BasePriority + Thread->AdjustIncrement; 00174 00175 /* Check if this new priority is higher */ 00176 if (OldPriority > Thread->Priority) 00177 { 00178 /* Make sure we don't go into the real time range */ 00179 if (OldPriority >= LOW_REALTIME_PRIORITY) 00180 { 00181 /* Normalize it back down one notch */ 00182 OldPriority = LOW_REALTIME_PRIORITY - 1; 00183 } 00184 00185 /* Check if the priority is higher then the boosted base */ 00186 if (OldPriority > (Thread->BasePriority + 00187 Thread->AdjustIncrement)) 00188 { 00189 /* Setup a priority decrement to nullify the boost */ 00190 Thread->PriorityDecrement = ((SCHAR)OldPriority - 00191 Thread->BasePriority - 00192 Thread->AdjustIncrement); 00193 } 00194 00195 /* Make sure that the priority decrement is valid */ 00196 ASSERT((Thread->PriorityDecrement >= 0) && 00197 (Thread->PriorityDecrement <= OldPriority)); 00198 00199 /* Set this new priority */ 00200 Thread->Priority = (SCHAR)OldPriority; 00201 } 00202 } 00203 } 00204 else 00205 { 00206 /* It's a real-time thread, so just reset its quantum */ 00207 Thread->Quantum = Thread->QuantumReset; 00208 } 00209 00210 /* Make sure the priority makes sense */ 00211 ASSERT((Thread->Priority >= 0) && (Thread->Priority <= HIGH_PRIORITY)); 00212 00213 /* Release the thread lock and reset the adjust reason */ 00214 KiReleaseThreadLock(Thread); 00215 Thread->AdjustReason = AdjustNone; 00216 } 00217 00218 /* Clear thread preemption status and save current values */ 00219 Preempted = Thread->Preempted; 00220 OldPriority = Thread->Priority; 00221 Thread->Preempted = FALSE; 00222 00223 /* Queue the thread on CPU 0 and get the PRCB and lock it */ 00224 Thread->NextProcessor = 0; 00225 Prcb = KiProcessorBlock[0]; 00226 KiAcquirePrcbLock(Prcb); 00227 00228 /* Check if we have an idle summary */ 00229 if (KiIdleSummary) 00230 { 00231 /* Clear it and set this thread as the next one */ 00232 KiIdleSummary = 0; 00233 Thread->State = Standby; 00234 Prcb->NextThread = Thread; 00235 00236 /* Unlock the PRCB and return */ 00237 KiReleasePrcbLock(Prcb); 00238 return; 00239 } 00240 00241 /* Set the CPU number */ 00242 Thread->NextProcessor = (UCHAR)Processor; 00243 00244 /* Get the next scheduled thread */ 00245 NextThread = Prcb->NextThread; 00246 if (NextThread) 00247 { 00248 /* Sanity check */ 00249 ASSERT(NextThread->State == Standby); 00250 00251 /* Check if priority changed */ 00252 if (OldPriority > NextThread->Priority) 00253 { 00254 /* Preempt the thread */ 00255 NextThread->Preempted = TRUE; 00256 00257 /* Put this one as the next one */ 00258 Thread->State = Standby; 00259 Prcb->NextThread = Thread; 00260 00261 /* Set it in deferred ready mode */ 00262 NextThread->State = DeferredReady; 00263 NextThread->DeferredProcessor = Prcb->Number; 00264 KiReleasePrcbLock(Prcb); 00265 KiDeferredReadyThread(NextThread); 00266 return; 00267 } 00268 } 00269 else 00270 { 00271 /* Set the next thread as the current thread */ 00272 NextThread = Prcb->CurrentThread; 00273 if (OldPriority > NextThread->Priority) 00274 { 00275 /* Preempt it if it's already running */ 00276 if (NextThread->State == Running) NextThread->Preempted = TRUE; 00277 00278 /* Set the thread on standby and as the next thread */ 00279 Thread->State = Standby; 00280 Prcb->NextThread = Thread; 00281 00282 /* Release the lock */ 00283 KiReleasePrcbLock(Prcb); 00284 00285 /* Check if we're running on another CPU */ 00286 if (KeGetCurrentProcessorNumber() != Thread->NextProcessor) 00287 { 00288 /* We are, send an IPI */ 00289 KiIpiSend(AFFINITY_MASK(Thread->NextProcessor), IPI_DPC); 00290 } 00291 return; 00292 } 00293 } 00294 00295 /* Sanity check */ 00296 ASSERT((OldPriority >= 0) && (OldPriority <= HIGH_PRIORITY)); 00297 00298 /* Set this thread as ready */ 00299 Thread->State = Ready; 00300 Thread->WaitTime = KeTickCount.LowPart; 00301 00302 /* Insert this thread in the appropriate order */ 00303 Preempted ? InsertHeadList(&Prcb->DispatcherReadyListHead[OldPriority], 00304 &Thread->WaitListEntry) : 00305 InsertTailList(&Prcb->DispatcherReadyListHead[OldPriority], 00306 &Thread->WaitListEntry); 00307 00308 /* Update the ready summary */ 00309 Prcb->ReadySummary |= PRIORITY_MASK(OldPriority); 00310 00311 /* Sanity check */ 00312 ASSERT(OldPriority == Thread->Priority); 00313 00314 /* Release the lock */ 00315 KiReleasePrcbLock(Prcb); 00316 } 00317 00318 PKTHREAD 00319 FASTCALL 00320 KiSelectNextThread(IN PKPRCB Prcb) 00321 { 00322 PKTHREAD Thread; 00323 00324 /* Select a ready thread */ 00325 Thread = KiSelectReadyThread(0, Prcb); 00326 if (!Thread) 00327 { 00328 /* Didn't find any, get the current idle thread */ 00329 Thread = Prcb->IdleThread; 00330 00331 /* Enable idle scheduling */ 00332 InterlockedOrSetMember(&KiIdleSummary, Prcb->SetMember); 00333 Prcb->IdleSchedule = TRUE; 00334 00335 /* FIXME: SMT support */ 00336 ASSERTMSG("SMP: Not yet implemented\n", FALSE); 00337 } 00338 00339 /* Sanity checks and return the thread */ 00340 ASSERT(Thread != NULL); 00341 ASSERT((Thread->BasePriority == 0) || (Thread->Priority != 0)); 00342 return Thread; 00343 } 00344 00345 LONG_PTR 00346 FASTCALL 00347 KiSwapThread(IN PKTHREAD CurrentThread, 00348 IN PKPRCB Prcb) 00349 { 00350 BOOLEAN ApcState = FALSE; 00351 KIRQL WaitIrql; 00352 LONG_PTR WaitStatus; 00353 PKTHREAD NextThread; 00354 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL); 00355 00356 /* Acquire the PRCB lock */ 00357 KiAcquirePrcbLock(Prcb); 00358 00359 /* Get the next thread */ 00360 NextThread = Prcb->NextThread; 00361 if (NextThread) 00362 { 00363 /* Already got a thread, set it up */ 00364 Prcb->NextThread = NULL; 00365 Prcb->CurrentThread = NextThread; 00366 NextThread->State = Running; 00367 } 00368 else 00369 { 00370 /* Try to find a ready thread */ 00371 NextThread = KiSelectReadyThread(0, Prcb); 00372 if (NextThread) 00373 { 00374 /* Switch to it */ 00375 Prcb->CurrentThread = NextThread; 00376 NextThread->State = Running; 00377 } 00378 else 00379 { 00380 /* Set the idle summary */ 00381 InterlockedOrSetMember(&KiIdleSummary, Prcb->SetMember); 00382 00383 /* Schedule the idle thread */ 00384 NextThread = Prcb->IdleThread; 00385 Prcb->CurrentThread = NextThread; 00386 NextThread->State = Running; 00387 } 00388 } 00389 00390 /* Sanity check and release the PRCB */ 00391 ASSERT(CurrentThread != Prcb->IdleThread); 00392 KiReleasePrcbLock(Prcb); 00393 00394 /* Save the wait IRQL */ 00395 WaitIrql = CurrentThread->WaitIrql; 00396 00397 /* Swap contexts */ 00398 ApcState = KiSwapContext(WaitIrql, CurrentThread); 00399 00400 /* Get the wait status */ 00401 WaitStatus = CurrentThread->WaitStatus; 00402 00403 /* Check if we need to deliver APCs */ 00404 if (ApcState) 00405 { 00406 /* Lower to APC_LEVEL */ 00407 KeLowerIrql(APC_LEVEL); 00408 00409 /* Deliver APCs */ 00410 KiDeliverApc(KernelMode, NULL, NULL); 00411 ASSERT(WaitIrql == 0); 00412 } 00413 00414 /* Lower IRQL back to what it was and return the wait status */ 00415 KeLowerIrql(WaitIrql); 00416 return WaitStatus; 00417 } 00418 00419 VOID 00420 NTAPI 00421 KiReadyThread(IN PKTHREAD Thread) 00422 { 00423 IN PKPROCESS Process = Thread->ApcState.Process; 00424 00425 /* Check if the process is paged out */ 00426 if (Process->State != ProcessInMemory) 00427 { 00428 /* We don't page out processes in ROS */ 00429 ASSERT(FALSE); 00430 } 00431 else if (!Thread->KernelStackResident) 00432 { 00433 /* Increase the stack count */ 00434 ASSERT(Process->StackCount != MAXULONG_PTR); 00435 Process->StackCount++; 00436 00437 /* Set the thread to transition */ 00438 ASSERT(Thread->State != Transition); 00439 Thread->State = Transition; 00440 00441 /* The stack is always resident in ROS */ 00442 ASSERT(FALSE); 00443 } 00444 else 00445 { 00446 /* Insert the thread on the deferred ready list */ 00447 KiInsertDeferredReadyList(Thread); 00448 } 00449 } 00450 00451 VOID 00452 NTAPI 00453 KiAdjustQuantumThread(IN PKTHREAD Thread) 00454 { 00455 PKPRCB Prcb = KeGetCurrentPrcb(); 00456 PKTHREAD NextThread; 00457 00458 /* Acquire thread and PRCB lock */ 00459 KiAcquireThreadLock(Thread); 00460 KiAcquirePrcbLock(Prcb); 00461 00462 /* Don't adjust for RT threads */ 00463 if ((Thread->Priority < LOW_REALTIME_PRIORITY) && 00464 (Thread->BasePriority < (LOW_REALTIME_PRIORITY - 2))) 00465 { 00466 /* Decrease Quantum by one and see if we've ran out */ 00467 if (--Thread->Quantum <= 0) 00468 { 00469 /* Return quantum */ 00470 Thread->Quantum = Thread->QuantumReset; 00471 00472 /* Calculate new Priority */ 00473 Thread->Priority = KiComputeNewPriority(Thread, 1); 00474 00475 /* Check if there's no next thread scheduled */ 00476 if (!Prcb->NextThread) 00477 { 00478 /* Select a ready thread and check if we found one */ 00479 NextThread = KiSelectReadyThread(Thread->Priority, Prcb); 00480 if (NextThread) 00481 { 00482 /* Set it on standby and switch to it */ 00483 NextThread->State = Standby; 00484 Prcb->NextThread = NextThread; 00485 } 00486 } 00487 else 00488 { 00489 /* This thread can be preempted again */ 00490 Thread->Preempted = FALSE; 00491 } 00492 } 00493 } 00494 00495 /* Release locks */ 00496 KiReleasePrcbLock(Prcb); 00497 KiReleaseThreadLock(Thread); 00498 KiExitDispatcher(Thread->WaitIrql); 00499 } 00500 00501 VOID 00502 FASTCALL 00503 KiSetPriorityThread(IN PKTHREAD Thread, 00504 IN KPRIORITY Priority) 00505 { 00506 PKPRCB Prcb; 00507 ULONG Processor; 00508 BOOLEAN RequestInterrupt = FALSE; 00509 KPRIORITY OldPriority; 00510 PKTHREAD NewThread; 00511 ASSERT((Priority >= 0) && (Priority <= HIGH_PRIORITY)); 00512 00513 /* Check if priority changed */ 00514 if (Thread->Priority != Priority) 00515 { 00516 /* Loop priority setting in case we need to start over */ 00517 for (;;) 00518 { 00519 /* Choose action based on thread's state */ 00520 if (Thread->State == Ready) 00521 { 00522 /* Make sure we're not on the ready queue */ 00523 if (!Thread->ProcessReadyQueue) 00524 { 00525 /* Get the PRCB for the thread and lock it */ 00526 Processor = Thread->NextProcessor; 00527 Prcb = KiProcessorBlock[Processor]; 00528 KiAcquirePrcbLock(Prcb); 00529 00530 /* Make sure the thread is still ready and on this CPU */ 00531 if ((Thread->State == Ready) && 00532 (Thread->NextProcessor == Prcb->Number)) 00533 { 00534 /* Sanity check */ 00535 ASSERT((Prcb->ReadySummary & 00536 PRIORITY_MASK(Thread->Priority))); 00537 00538 /* Remove it from the current queue */ 00539 if (RemoveEntryList(&Thread->WaitListEntry)) 00540 { 00541 /* Update the ready summary */ 00542 Prcb->ReadySummary ^= PRIORITY_MASK(Thread-> 00543 Priority); 00544 } 00545 00546 /* Update priority */ 00547 Thread->Priority = (SCHAR)Priority; 00548 00549 /* Re-insert it at its current priority */ 00550 KiInsertDeferredReadyList(Thread); 00551 00552 /* Release the PRCB Lock */ 00553 KiReleasePrcbLock(Prcb); 00554 } 00555 else 00556 { 00557 /* Release the lock and loop again */ 00558 KiReleasePrcbLock(Prcb); 00559 continue; 00560 } 00561 } 00562 else 00563 { 00564 /* It's already on the ready queue, just update priority */ 00565 Thread->Priority = (SCHAR)Priority; 00566 } 00567 } 00568 else if (Thread->State == Standby) 00569 { 00570 /* Get the PRCB for the thread and lock it */ 00571 Processor = Thread->NextProcessor; 00572 Prcb = KiProcessorBlock[Processor]; 00573 KiAcquirePrcbLock(Prcb); 00574 00575 /* Check if we're still the next thread to run */ 00576 if (Thread == Prcb->NextThread) 00577 { 00578 /* Get the old priority and update ours */ 00579 OldPriority = Thread->Priority; 00580 Thread->Priority = (SCHAR)Priority; 00581 00582 /* Check if there was a change */ 00583 if (Priority < OldPriority) 00584 { 00585 /* Find a new thread */ 00586 NewThread = KiSelectReadyThread(Priority + 1, Prcb); 00587 if (NewThread) 00588 { 00589 /* Found a new one, set it on standby */ 00590 NewThread->State = Standby; 00591 Prcb->NextThread = NewThread; 00592 00593 /* Dispatch our thread */ 00594 KiInsertDeferredReadyList(Thread); 00595 } 00596 } 00597 00598 /* Release the PRCB lock */ 00599 KiReleasePrcbLock(Prcb); 00600 } 00601 else 00602 { 00603 /* Release the lock and try again */ 00604 KiReleasePrcbLock(Prcb); 00605 continue; 00606 } 00607 } 00608 else if (Thread->State == Running) 00609 { 00610 /* Get the PRCB for the thread and lock it */ 00611 Processor = Thread->NextProcessor; 00612 Prcb = KiProcessorBlock[Processor]; 00613 KiAcquirePrcbLock(Prcb); 00614 00615 /* Check if we're still the current thread running */ 00616 if (Thread == Prcb->CurrentThread) 00617 { 00618 /* Get the old priority and update ours */ 00619 OldPriority = Thread->Priority; 00620 Thread->Priority = (SCHAR)Priority; 00621 00622 /* Check if there was a change and there's no new thread */ 00623 if ((Priority < OldPriority) && !(Prcb->NextThread)) 00624 { 00625 /* Find a new thread */ 00626 NewThread = KiSelectReadyThread(Priority + 1, Prcb); 00627 if (NewThread) 00628 { 00629 /* Found a new one, set it on standby */ 00630 NewThread->State = Standby; 00631 Prcb->NextThread = NewThread; 00632 00633 /* Request an interrupt */ 00634 RequestInterrupt = TRUE; 00635 } 00636 } 00637 00638 /* Release the lock and check if we need an interrupt */ 00639 KiReleasePrcbLock(Prcb); 00640 if (RequestInterrupt) 00641 { 00642 /* Check if we're running on another CPU */ 00643 if (KeGetCurrentProcessorNumber() != Processor) 00644 { 00645 /* We are, send an IPI */ 00646 KiIpiSend(AFFINITY_MASK(Processor), IPI_DPC); 00647 } 00648 } 00649 } 00650 else 00651 { 00652 /* Thread changed, release lock and restart */ 00653 KiReleasePrcbLock(Prcb); 00654 continue; 00655 } 00656 } 00657 else if (Thread->State == DeferredReady) 00658 { 00659 /* FIXME: TODO */ 00660 DPRINT1("Deferred state not yet supported\n"); 00661 ASSERT(FALSE); 00662 } 00663 else 00664 { 00665 /* Any other state, just change priority */ 00666 Thread->Priority = (SCHAR)Priority; 00667 } 00668 00669 /* If we got here, then thread state was consistent, so bail out */ 00670 break; 00671 } 00672 } 00673 } 00674 00675 KAFFINITY 00676 FASTCALL 00677 KiSetAffinityThread(IN PKTHREAD Thread, 00678 IN KAFFINITY Affinity) 00679 { 00680 KAFFINITY OldAffinity; 00681 00682 /* Get the current affinity */ 00683 OldAffinity = Thread->UserAffinity; 00684 00685 /* Make sure that the affinity is valid */ 00686 if (((Affinity & Thread->ApcState.Process->Affinity) != (Affinity)) || 00687 (!Affinity)) 00688 { 00689 /* Bugcheck the system */ 00690 KeBugCheck(INVALID_AFFINITY_SET); 00691 } 00692 00693 /* Update the new affinity */ 00694 Thread->UserAffinity = Affinity; 00695 00696 /* Check if system affinity is disabled */ 00697 if (!Thread->SystemAffinityActive) 00698 { 00699 /* FIXME: TODO */ 00700 DPRINT1("Affinity support disabled!\n"); 00701 } 00702 00703 /* Return the old affinity */ 00704 return OldAffinity; 00705 } 00706 00707 // 00708 // This macro exists because NtYieldExecution locklessly attempts to read from 00709 // the KPRCB's ready summary, and the usual way of going through KeGetCurrentPrcb 00710 // would require getting fs:1C first (or gs), and then doing another dereference. 00711 // In an attempt to minimize the amount of instructions and potential race/tear 00712 // that could happen, Windows seems to define this as a macro that directly acceses 00713 // the ready summary through a single fs: read by going through the KPCR's PrcbData. 00714 // 00715 // See http://research.microsoft.com/en-us/collaboration/global/asia-pacific/ 00716 // programs/trk_case4_process-thread_management.pdf 00717 // 00718 // We need this per-arch because sometimes it's Prcb and sometimes PrcbData, and 00719 // because on x86 it's FS, and on x64 it's GS (not sure what it is on ARM/PPC). 00720 // 00721 #ifdef _M_IX86 00722 #define KiGetCurrentReadySummary() __readfsdword(FIELD_OFFSET(KIPCR, PrcbData.ReadySummary)) 00723 #elif _M_AMD64 00724 #define KiGetCurrentReadySummary() __readgsdword(FIELD_OFFSET(KIPCR, Prcb.ReadySummary)) 00725 #else 00726 #error Implement me! 00727 #endif 00728 00729 /* 00730 * @implemented 00731 */ 00732 NTSTATUS 00733 NTAPI 00734 NtYieldExecution(VOID) 00735 { 00736 NTSTATUS Status; 00737 KIRQL OldIrql; 00738 PKPRCB Prcb; 00739 PKTHREAD Thread, NextThread; 00740 00741 /* NB: No instructions (other than entry code) should preceed this line */ 00742 00743 /* Fail if there's no ready summary */ 00744 if (!KiGetCurrentReadySummary()) return STATUS_NO_YIELD_PERFORMED; 00745 00746 /* Now get the current thread, set the status... */ 00747 Status = STATUS_NO_YIELD_PERFORMED; 00748 Thread = KeGetCurrentThread(); 00749 00750 /* Raise IRQL to synch and get the KPRCB now */ 00751 OldIrql = KeRaiseIrqlToSynchLevel(); 00752 Prcb = KeGetCurrentPrcb(); 00753 00754 /* Now check if there's still a ready summary */ 00755 if (Prcb->ReadySummary) 00756 { 00757 /* Acquire thread and PRCB lock */ 00758 KiAcquireThreadLock(Thread); 00759 KiAcquirePrcbLock(Prcb); 00760 00761 /* Find a new thread to run if none was selected */ 00762 if (!Prcb->NextThread) Prcb->NextThread = KiSelectReadyThread(1, Prcb); 00763 00764 /* Make sure we still have a next thread to schedule */ 00765 NextThread = Prcb->NextThread; 00766 if (NextThread) 00767 { 00768 /* Reset quantum and recalculate priority */ 00769 Thread->Quantum = Thread->QuantumReset; 00770 Thread->Priority = KiComputeNewPriority(Thread, 1); 00771 00772 /* Release the thread lock */ 00773 KiReleaseThreadLock(Thread); 00774 00775 /* Set context swap busy */ 00776 KiSetThreadSwapBusy(Thread); 00777 00778 /* Set the new thread as running */ 00779 Prcb->NextThread = NULL; 00780 Prcb->CurrentThread = NextThread; 00781 NextThread->State = Running; 00782 00783 /* Setup a yield wait and queue the thread */ 00784 Thread->WaitReason = WrYieldExecution; 00785 KxQueueReadyThread(Thread, Prcb); 00786 00787 /* Make it wait at APC_LEVEL */ 00788 Thread->WaitIrql = APC_LEVEL; 00789 00790 /* Sanity check */ 00791 ASSERT(OldIrql <= DISPATCH_LEVEL); 00792 00793 /* Swap to new thread */ 00794 KiSwapContext(APC_LEVEL, Thread); 00795 Status = STATUS_SUCCESS; 00796 } 00797 else 00798 { 00799 /* Release the PRCB and thread lock */ 00800 KiReleasePrcbLock(Prcb); 00801 KiReleaseThreadLock(Thread); 00802 } 00803 } 00804 00805 /* Lower IRQL and return */ 00806 KeLowerIrql(OldIrql); 00807 return Status; 00808 } Generated on Sun May 27 2012 04:37:33 for ReactOS by
1.7.6.1
|