Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

975 lines
23 KiB

  1. /*++
  2. Copyright (c) 1991-1994 Microsoft Corporation
  3. Module Name:
  4. balmgr.c
  5. Abstract:
  6. This module implements the NT balance set manager. Normally the kernel
  7. does not contain "policy" code. However, the balance set manager needs
  8. to be able to traverse the kernel data structures and, therefore, the
  9. code has been located as logically part of the kernel.
  10. The balance set manager performs the following operations:
  11. 1. Makes the kernel stack of threads that have been waiting for a
  12. certain amount of time, nonresident.
  13. 2. Removes processes from the balance set when memory gets tight
  14. and brings processes back into the balance set when there is
  15. more memory available.
  16. 3. Makes the kernel stack resident for threads whose wait has been
  17. completed, but whose stack is nonresident.
  18. 4. Arbitrarily boosts the priority of a selected set of threads
  19. to prevent priority inversion in variable priority levels.
  20. In general, the balance set manager only is active during periods when
  21. memory is tight.
  22. Author:
  23. David N. Cutler (davec) 13-Jul-1991
  24. Environment:
  25. Kernel mode only.
  26. Revision History:
  27. --*/
  28. #include "ki.h"
  29. //
  30. // Define balance set wait object types.
  31. //
  32. typedef enum _BALANCE_OBJECT {
  33. TimerExpiration,
  34. WorkingSetManagerEvent,
  35. MaximumObject
  36. } BALANCE_OBJECT;
  37. //
  38. // Define maximum number of thread stacks that can be out swapped in
  39. // a single time period.
  40. //
  41. #define MAXIMUM_THREAD_STACKS 5
  42. //
  43. // Define periodic wait interval value.
  44. //
  45. #define PERIODIC_INTERVAL (1 * 1000 * 1000 * 10)
  46. //
  47. // Define amount of time a thread can be in the ready state without having
  48. // is priority boosted (approximately 4 seconds).
  49. //
  50. #define READY_WITHOUT_RUNNING (4 * 75)
  51. //
  52. // Define kernel stack protect time. For small systems the protect time
  53. // is 3 seconds. For all other systems, the protect time is 5x seconds.
  54. //
  55. #define SMALL_SYSTEM_STACK_PROTECT_TIME (3 * 75)
  56. #define LARGE_SYSTEM_STACK_PROTECT_TIME (SMALL_SYSTEM_STACK_PROTECT_TIME * 5)
  57. #define STACK_SCAN_PERIOD 4
  58. ULONG KiStackProtectTime;
  59. //
  60. // Define number of threads to scan each period and the priority boost bias.
  61. //
  62. #define THREAD_BOOST_BIAS 1
  63. #define THREAD_BOOST_PRIORITY (LOW_REALTIME_PRIORITY - THREAD_BOOST_BIAS)
  64. #define THREAD_SCAN_PRIORITY (THREAD_BOOST_PRIORITY - 1)
  65. #define THREAD_READY_COUNT 10
  66. #define THREAD_SCAN_COUNT 16
  67. //
  68. // Define local procedure prototypes.
  69. //
  70. VOID
  71. KiAdjustIrpCredits (
  72. VOID
  73. );
  74. VOID
  75. KiInSwapKernelStacks (
  76. IN PSINGLE_LIST_ENTRY SwapEntry
  77. );
  78. VOID
  79. KiInSwapProcesses (
  80. IN PSINGLE_LIST_ENTRY SwapEntry
  81. );
  82. VOID
  83. KiOutSwapKernelStacks (
  84. VOID
  85. );
  86. VOID
  87. KiOutSwapProcesses (
  88. IN PSINGLE_LIST_ENTRY SwapEntry
  89. );
  90. VOID
  91. KiScanReadyQueues (
  92. VOID
  93. );
  94. //
  95. // Define global IRP credit adjustment data.
  96. //
  97. #if !defined(NT_UP)
  98. LONG KiLastProcessor = 0;
  99. #endif
  100. //
  101. // Define thread table index static data.
  102. //
  103. ULONG KiReadyQueueIndex = 1;
  104. //
  105. // Define swap request flag.
  106. //
  107. LONG KiStackOutSwapRequest = FALSE;
  108. VOID
  109. KeBalanceSetManager (
  110. IN PVOID Context
  111. )
  112. /*++
  113. Routine Description:
  114. This function is the startup code for the balance set manager. The
  115. balance set manager thread is created during system initialization
  116. and begins execution in this function.
  117. Arguments:
  118. Context - Supplies a pointer to an arbitrary data structure (NULL).
  119. Return Value:
  120. None.
  121. --*/
  122. {
  123. LARGE_INTEGER DueTime;
  124. KIRQL OldIrql;
  125. KTIMER PeriodTimer;
  126. ULONG StackScanPeriod;
  127. NTSTATUS Status;
  128. PKTHREAD Thread;
  129. KWAIT_BLOCK WaitBlockArray[MaximumObject];
  130. PVOID WaitObjects[MaximumObject];
  131. //
  132. // Raise the thread priority to the lowest realtime level.
  133. //
  134. KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
  135. //
  136. // Initialize the periodic timer, set it to expire one period from
  137. // now, and set the stack scan period.
  138. //
  139. KeInitializeTimer(&PeriodTimer);
  140. DueTime.QuadPart = - PERIODIC_INTERVAL;
  141. KeSetTimer(&PeriodTimer, DueTime, NULL);
  142. StackScanPeriod = STACK_SCAN_PERIOD;
  143. //
  144. // Compute the stack protect time based on the system size.
  145. //
  146. if (MmQuerySystemSize() == MmSmallSystem) {
  147. KiStackProtectTime = SMALL_SYSTEM_STACK_PROTECT_TIME;
  148. } else {
  149. KiStackProtectTime = LARGE_SYSTEM_STACK_PROTECT_TIME;
  150. }
  151. //
  152. // Initialize the wait objects array.
  153. //
  154. WaitObjects[TimerExpiration] = (PVOID)&PeriodTimer;
  155. WaitObjects[WorkingSetManagerEvent] = (PVOID)&MmWorkingSetManagerEvent;
  156. //
  157. // Loop forever processing balance set manager events.
  158. //
  159. do {
  160. //
  161. // Wait for a memory management memory low event, a swap event,
  162. // or the expiration of the period timout rate that the balance
  163. // set manager runs at.
  164. //
  165. Status = KeWaitForMultipleObjects(MaximumObject,
  166. &WaitObjects[0],
  167. WaitAny,
  168. Executive,
  169. KernelMode,
  170. FALSE,
  171. NULL,
  172. &WaitBlockArray[0]);
  173. //
  174. // Switch on the wait status.
  175. //
  176. switch (Status) {
  177. //
  178. // Periodic timer expiration.
  179. //
  180. case TimerExpiration:
  181. //
  182. // Adjust I/O lookaside credits.
  183. //
  184. #if !defined(NT_UP)
  185. KiAdjustIrpCredits();
  186. #endif
  187. //
  188. // Adjust the depth of lookaside lists.
  189. //
  190. ExAdjustLookasideDepth();
  191. //
  192. // Scan ready queues and boost thread priorities as appropriate.
  193. //
  194. KiScanReadyQueues();
  195. //
  196. // Execute the virtual memory working set manager.
  197. //
  198. MmWorkingSetManager();
  199. //
  200. // Set the timer to expire at the next periodic interval.
  201. //
  202. KeSetTimer(&PeriodTimer, DueTime, NULL);
  203. //
  204. // Attempt to initiate outswaping of kernel stacks.
  205. //
  206. // N.B. If outswapping is initiated, then the dispatcher
  207. // lock is not released until the wait at the top
  208. // of the loop is executed.
  209. //
  210. StackScanPeriod -= 1;
  211. if (StackScanPeriod == 0) {
  212. StackScanPeriod = STACK_SCAN_PERIOD;
  213. if (InterlockedCompareExchange(&KiStackOutSwapRequest,
  214. TRUE,
  215. FALSE) == FALSE) {
  216. KiLockDispatcherDatabase(&OldIrql);
  217. KiSetSwapEvent();
  218. Thread = KeGetCurrentThread();
  219. Thread->WaitNext = TRUE;
  220. Thread->WaitIrql = OldIrql;
  221. }
  222. }
  223. break;
  224. //
  225. // Working set manager event.
  226. //
  227. case WorkingSetManagerEvent:
  228. //
  229. // Call the working set manager to trim working sets.
  230. //
  231. MmWorkingSetManager();
  232. break;
  233. //
  234. // Illegal return status.
  235. //
  236. default:
  237. KdPrint(("BALMGR: Illegal wait status, %lx =\n", Status));
  238. break;
  239. }
  240. } while (TRUE);
  241. return;
  242. }
  243. VOID
  244. KeSwapProcessOrStack (
  245. IN PVOID Context
  246. )
  247. /*++
  248. Routine Description:
  249. This thread controls the swapping of processes and kernel stacks. The
  250. order of evaluation is:
  251. Outswap kernel stacks
  252. Outswap processes
  253. Inswap processes
  254. Inswap kernel stacks
  255. Arguments:
  256. Context - Supplies a pointer to the routine context - not used.
  257. Return Value:
  258. None.
  259. --*/
  260. {
  261. PSINGLE_LIST_ENTRY SwapEntry;
  262. //
  263. // Set address of swap thread object and raise the thread priority to
  264. // the lowest realtime level + 7 (i.e., priority 23).
  265. //
  266. KiSwappingThread = KeGetCurrentThread();
  267. KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY + 7);
  268. //
  269. // Loop for ever processing swap events.
  270. //
  271. // N.B. This is the ONLY thread that processes swap events.
  272. //
  273. do {
  274. //
  275. // Wait for a swap event to occur.
  276. //
  277. KeWaitForSingleObject(&KiSwapEvent,
  278. Executive,
  279. KernelMode,
  280. FALSE,
  281. NULL);
  282. //
  283. // The following events are processed one after the other. If
  284. // another event of a particular type arrives after having
  285. // processed the respective event type, them the swap event
  286. // will have been set and the above wait will immediately be
  287. // satisifed.
  288. //
  289. // Check to determine if there is a kernel stack out swap scan
  290. // request pending.
  291. //
  292. if (InterlockedCompareExchange(&KiStackOutSwapRequest,
  293. FALSE,
  294. TRUE) == TRUE) {
  295. KiOutSwapKernelStacks();
  296. }
  297. //
  298. // Check if there are any process out swap requests pending.
  299. //
  300. SwapEntry = InterlockedFlushSingleList(&KiProcessOutSwapListHead);
  301. if (SwapEntry != NULL) {
  302. KiOutSwapProcesses(SwapEntry);
  303. }
  304. //
  305. // Check if there are any process in swap requests pending.
  306. //
  307. SwapEntry = InterlockedFlushSingleList(&KiProcessInSwapListHead);
  308. if (SwapEntry != NULL) {
  309. KiInSwapProcesses(SwapEntry);
  310. }
  311. //
  312. // Check if there are any kernel stack in swap requests pending.
  313. //
  314. SwapEntry = InterlockedFlushSingleList(&KiStackInSwapListHead);
  315. if (SwapEntry != NULL) {
  316. KiInSwapKernelStacks(SwapEntry);
  317. }
  318. } while (TRUE);
  319. return;
  320. }
  321. #if !defined(NT_UP)
  322. VOID
  323. KiAdjustIrpCredits (
  324. VOID
  325. )
  326. /*++
  327. Routine Description:
  328. This function adjusts the lookaside IRP float credits for two processors
  329. during each one second scan interval. IRP credits are adjusted by using
  330. a moving average of two processors. It is possible for the IRP credits
  331. for a processor to become negative, but this condition will be self
  332. correcting.
  333. Arguments:
  334. None.
  335. Return Value:
  336. None.
  337. --*/
  338. {
  339. LONG Average;
  340. LONG Adjust;
  341. LONG Last;
  342. LONG LastCount;
  343. PKPRCB LastPrcb;
  344. LONG Next;
  345. LONG NextCount;
  346. PKPRCB NextPrcb;
  347. //
  348. // Compute the processor number of the next processor.
  349. //
  350. Last = KiLastProcessor;
  351. Next = Last + 1;
  352. if (Next >= KeNumberProcessors) {
  353. Next = 0;
  354. }
  355. //
  356. // Compute the average IRP credits of the two processors and apply an
  357. // adjustment if the average is greater than zero.
  358. //
  359. LastPrcb = KiProcessorBlock[Last];
  360. NextPrcb = KiProcessorBlock[Next];
  361. LastCount = LastPrcb->LookasideIrpFloat;
  362. NextCount = NextPrcb->LookasideIrpFloat;
  363. Average = (LastCount + NextCount) >> 1;
  364. if (Average > 0) {
  365. //
  366. // If the last count is greater than the average, then adjust by
  367. // an amount equal to the last count minus the average. Otherwise,
  368. // adjust by an amount equal to the next count minus the average.
  369. //
  370. if (LastCount > Average ) {
  371. Adjust = Average - LastCount;
  372. } else {
  373. Adjust = NextCount - Average;
  374. }
  375. InterlockedExchangeAdd(&LastPrcb->LookasideIrpFloat, Adjust);
  376. InterlockedExchangeAdd(&NextPrcb->LookasideIrpFloat, -Adjust);
  377. }
  378. //
  379. // Set new last processor for next scan.
  380. //
  381. KiLastProcessor = Next;
  382. return;
  383. }
  384. #endif
  385. VOID
  386. KiInSwapKernelStacks (
  387. IN PSINGLE_LIST_ENTRY SwapEntry
  388. )
  389. /*++
  390. Routine Description:
  391. This function in swaps the kernel stack for threads whose wait has been
  392. completed and whose kernel stack is nonresident.
  393. Arguments:
  394. SwapEntry - Supplies a pointer to the first entry in the in swap list.
  395. Return Value:
  396. None.
  397. --*/
  398. {
  399. KIRQL OldIrql;
  400. PKTHREAD Thread;
  401. //
  402. // Process the stack in swap SLIST and for each thread removed from the
  403. // SLIST, make its kernel stack resident, and ready it for execution.
  404. //
  405. do {
  406. Thread = CONTAINING_RECORD(SwapEntry, KTHREAD, SwapListEntry);
  407. SwapEntry = SwapEntry->Next;
  408. MmInPageKernelStack(Thread);
  409. KiLockDispatcherDatabase(&OldIrql);
  410. Thread->KernelStackResident = TRUE;
  411. KiReadyThread(Thread);
  412. KiUnlockDispatcherDatabase(OldIrql);
  413. } while (SwapEntry != NULL);
  414. return;
  415. }
  416. VOID
  417. KiInSwapProcesses (
  418. IN PSINGLE_LIST_ENTRY SwapEntry
  419. )
  420. /*++
  421. Routine Description:
  422. This function in swaps processes.
  423. Arguments:
  424. SwapEntry - Supplies a pointer to the first entry in the SLIST.
  425. Return Value:
  426. None.
  427. --*/
  428. {
  429. PLIST_ENTRY NextEntry;
  430. KIRQL OldIrql;
  431. PKPROCESS Process;
  432. PKTHREAD Thread;
  433. //
  434. // Process the process in swap list and for each process removed from
  435. // the list, make the process resident, and process its ready list.
  436. //
  437. do {
  438. Process = CONTAINING_RECORD(SwapEntry, KPROCESS, SwapListEntry);
  439. SwapEntry = SwapEntry->Next;
  440. Process->State = ProcessInSwap;
  441. MmInSwapProcess(Process);
  442. KiLockDispatcherDatabase(&OldIrql);
  443. Process->State = ProcessInMemory;
  444. NextEntry = Process->ReadyListHead.Flink;
  445. while (NextEntry != &Process->ReadyListHead) {
  446. Thread = CONTAINING_RECORD(NextEntry, KTHREAD, WaitListEntry);
  447. RemoveEntryList(NextEntry);
  448. Thread->ProcessReadyQueue = FALSE;
  449. KiReadyThread(Thread);
  450. NextEntry = Process->ReadyListHead.Flink;
  451. }
  452. KiUnlockDispatcherDatabase(OldIrql);
  453. } while (SwapEntry != NULL);
  454. return;
  455. }
  456. VOID
  457. KiOutSwapKernelStacks (
  458. VOID
  459. )
  460. /*++
  461. Routine Description:
  462. This function attempts to out swap the kernel stack for threads whose
  463. wait mode is user and which have been waiting longer than the stack
  464. protect time.
  465. Arguments:
  466. None.
  467. Return Value:
  468. None.
  469. --*/
  470. {
  471. PLIST_ENTRY NextEntry;
  472. ULONG NumberOfThreads;
  473. KIRQL OldIrql;
  474. PKPROCESS Process;
  475. PKTHREAD Thread;
  476. PKTHREAD ThreadObjects[MAXIMUM_THREAD_STACKS];
  477. ULONG WaitLimit;
  478. //
  479. // Scan the waiting in list and check if the wait time exceeds the
  480. // stack protect time. If the protect time is exceeded, then make
  481. // the kernel stack of the waiting thread nonresident. If the count
  482. // of the number of stacks that are resident for the process reaches
  483. // zero, then insert the process in the outswap list and set its state
  484. // to transition.
  485. //
  486. // Raise IRQL and lock the dispatcher database.
  487. //
  488. NumberOfThreads = 0;
  489. WaitLimit = KiQueryLowTickCount() - KiStackProtectTime;
  490. KiLockDispatcherDatabase(&OldIrql);
  491. NextEntry = KiWaitListHead.Flink;
  492. while ((NextEntry != &KiWaitListHead) &&
  493. (NumberOfThreads < MAXIMUM_THREAD_STACKS)) {
  494. Thread = CONTAINING_RECORD(NextEntry, KTHREAD, WaitListEntry);
  495. ASSERT(Thread->WaitMode == UserMode);
  496. NextEntry = NextEntry->Flink;
  497. //
  498. // Threads are inserted at the end of the wait list in very nearly
  499. // reverse time order, i.e., the longest waiting thread is at the
  500. // beginning of the list followed by the next oldest, etc. Thus when
  501. // a thread is encountered which still has a protected stack it is
  502. // known that all further threads in the wait also have protected
  503. // stacks, and therefore, the scan can be terminated.
  504. //
  505. // N.B. It is possible due to a race condition in wait that a high
  506. // priority thread was placed in the wait list. If this occurs,
  507. // then the thread is removed from the wait list without swapping
  508. // the stack.
  509. //
  510. if (WaitLimit < Thread->WaitTime) {
  511. break;
  512. } else if (Thread->Priority >= (LOW_REALTIME_PRIORITY + 9)) {
  513. RemoveEntryList(&Thread->WaitListEntry);
  514. Thread->WaitListEntry.Flink = NULL;
  515. } else if (KiIsThreadNumericStateSaved(Thread)) {
  516. Thread->KernelStackResident = FALSE;
  517. ThreadObjects[NumberOfThreads] = Thread;
  518. NumberOfThreads += 1;
  519. RemoveEntryList(&Thread->WaitListEntry);
  520. Thread->WaitListEntry.Flink = NULL;
  521. Process = Thread->ApcState.Process;
  522. Process->StackCount -= 1;
  523. if (Process->StackCount == 0) {
  524. Process->State = ProcessOutTransition;
  525. InterlockedPushEntrySingleList(&KiProcessOutSwapListHead,
  526. &Process->SwapListEntry);
  527. KiSwapEvent.Header.SignalState = 1;
  528. }
  529. }
  530. }
  531. //
  532. // Unlock the dispatcher database and lower IRQL to its previous
  533. // value.
  534. //
  535. KiUnlockDispatcherDatabase(OldIrql);
  536. //
  537. // Out swap the kernel stack for the selected set of threads.
  538. //
  539. while (NumberOfThreads > 0) {
  540. NumberOfThreads -= 1;
  541. Thread = ThreadObjects[NumberOfThreads];
  542. MmOutPageKernelStack(Thread);
  543. }
  544. return;
  545. }
  546. VOID
  547. KiOutSwapProcesses (
  548. IN PSINGLE_LIST_ENTRY SwapEntry
  549. )
  550. /*++
  551. Routine Description:
  552. This function out swaps processes.
  553. Arguments:
  554. SwapEntry - Supplies a pointer to the first entry in the SLIST.
  555. Return Value:
  556. None.
  557. --*/
  558. {
  559. PLIST_ENTRY NextEntry;
  560. KIRQL OldIrql;
  561. PKPROCESS Process;
  562. PKTHREAD Thread;
  563. //
  564. // Process the process out swap list and for each process removed from
  565. // the list, make the process nonresident, and process its ready list.
  566. //
  567. do {
  568. Process = CONTAINING_RECORD(SwapEntry, KPROCESS, SwapListEntry);
  569. SwapEntry = SwapEntry->Next;
  570. //
  571. // If there are any threads in the process ready list, then don't
  572. // out swap the process and ready all threads in the process ready
  573. // list. Otherwise, out swap the process.
  574. //
  575. KiLockDispatcherDatabase(&OldIrql);
  576. NextEntry = Process->ReadyListHead.Flink;
  577. if (NextEntry != &Process->ReadyListHead) {
  578. Process->State = ProcessInMemory;
  579. while (NextEntry != &Process->ReadyListHead) {
  580. Thread = CONTAINING_RECORD(NextEntry, KTHREAD, WaitListEntry);
  581. RemoveEntryList(NextEntry);
  582. Thread->ProcessReadyQueue = FALSE;
  583. KiReadyThread(Thread);
  584. NextEntry = Process->ReadyListHead.Flink;
  585. }
  586. KiUnlockDispatcherDatabase(OldIrql);
  587. } else {
  588. Process->State = ProcessOutSwap;
  589. KiUnlockDispatcherDatabase(OldIrql);
  590. MmOutSwapProcess(Process);
  591. //
  592. // While the process was being outswapped there may have been one
  593. // or more threads that attached to the process. If the process
  594. // ready list is not empty, then in swap the process. Otherwise,
  595. // mark the process as out of memory.
  596. //
  597. KiLockDispatcherDatabase(&OldIrql);
  598. NextEntry = Process->ReadyListHead.Flink;
  599. if (NextEntry != &Process->ReadyListHead) {
  600. Process->State = ProcessInTransition;
  601. InterlockedPushEntrySingleList(&KiProcessInSwapListHead,
  602. &Process->SwapListEntry);
  603. KiSwapEvent.Header.SignalState = 1;
  604. } else {
  605. Process->State = ProcessOutOfMemory;
  606. }
  607. KiUnlockDispatcherDatabase(OldIrql);
  608. }
  609. } while (SwapEntry != NULL);
  610. return;
  611. }
  612. VOID
  613. KiScanReadyQueues (
  614. VOID
  615. )
  616. /*++
  617. Routine Description:
  618. This function scans a section of the ready queues and attempts to
  619. boost the priority of threads that run at variable priority levels.
  620. Arguments:
  621. None.
  622. Return Value:
  623. None.
  624. --*/
  625. {
  626. ULONG Count = 0;
  627. PLIST_ENTRY Entry;
  628. ULONG Index;
  629. PLIST_ENTRY ListHead;
  630. ULONG Mask;
  631. ULONG Number = 0;
  632. KIRQL OldIrql;
  633. PKPROCESS Process;
  634. ULONG Summary;
  635. PKTHREAD Thread;
  636. ULONG WaitLimit;
  637. //
  638. // Lock the dispatcher database and check if there are any ready threads
  639. // queued at the scanable priority levels.
  640. //
  641. Index = KiReadyQueueIndex;
  642. Count = THREAD_READY_COUNT;
  643. Mask = 1 << Index;
  644. Number = THREAD_SCAN_COUNT;
  645. WaitLimit = KiQueryLowTickCount() - READY_WITHOUT_RUNNING;
  646. KiLockDispatcherDatabase(&OldIrql);
  647. Summary = KiReadySummary & ((1 << THREAD_BOOST_PRIORITY) - 2);
  648. if (Summary != 0) {
  649. do {
  650. //
  651. // If the current ready queue index is beyond the end of the range
  652. // of priorities that are scanned, then wrap back to the beginning
  653. // priority.
  654. //
  655. if (Index > THREAD_SCAN_PRIORITY) {
  656. Index = 1;
  657. Mask = 2;
  658. }
  659. //
  660. // If there are any ready threads queued at the current priority
  661. // level, then attempt to boost the thread priority.
  662. //
  663. if ((Summary & Mask) != 0) {
  664. Summary ^= Mask;
  665. ListHead = &KiDispatcherReadyListHead[Index];
  666. Entry = ListHead->Flink;
  667. ASSERT(Entry != ListHead);
  668. do {
  669. //
  670. // If the thread has been waiting for an extended period,
  671. // then boost the priority of the selected.
  672. //
  673. Thread = CONTAINING_RECORD(Entry, KTHREAD, WaitListEntry);
  674. if (WaitLimit >= Thread->WaitTime) {
  675. //
  676. // Remove the thread from the respective ready queue.
  677. //
  678. Entry = Entry->Blink;
  679. RemoveEntryList(Entry->Flink);
  680. if (IsListEmpty(ListHead) != FALSE) {
  681. KiReadySummary ^= Mask;
  682. }
  683. //
  684. // Compute the priority decrement value, set the new
  685. // thread priority, set the decrement count, set the
  686. // thread quantum, and ready the thread for execution.
  687. //
  688. Thread->PriorityDecrement +=
  689. THREAD_BOOST_PRIORITY - Thread->Priority;
  690. Thread->DecrementCount = ROUND_TRIP_DECREMENT_COUNT;
  691. Thread->Priority = THREAD_BOOST_PRIORITY;
  692. Process = Thread->ApcState.Process;
  693. Thread->Quantum = Process->ThreadQuantum * 2;
  694. KiReadyThread(Thread);
  695. Count -= 1;
  696. }
  697. Entry = Entry->Flink;
  698. Number -= 1;
  699. } while ((Entry != ListHead) && (Number != 0) && (Count != 0));
  700. }
  701. Index += 1;
  702. Mask <<= 1;
  703. } while ((Summary != 0) && (Number != 0) && (Count != 0));
  704. }
  705. //
  706. // Unlock the dispatcher database and save the last read queue index
  707. // for the next scan.
  708. //
  709. KiUnlockDispatcherDatabase(OldIrql);
  710. if ((Count != 0) && (Number != 0)) {
  711. KiReadyQueueIndex = 1;
  712. } else {
  713. KiReadyQueueIndex = Index;
  714. }
  715. return;
  716. }