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.

449 lines
11 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. waitsup.c
  5. Abstract:
  6. This module contains the support routines necessary to support the
  7. generic kernel wait functions. Functions are provided to test if a
  8. wait can be satisfied, to satisfy a wait, and to unwwait a thread.
  9. Author:
  10. David N. Cutler (davec) 24-Mar-1989
  11. Environment:
  12. Kernel mode only.
  13. Revision History:
  14. --*/
  15. #include "ki.h"
  16. VOID
  17. FASTCALL
  18. KiUnlinkThread (
  19. IN PRKTHREAD Thread,
  20. IN LONG_PTR WaitStatus
  21. )
  22. /*++
  23. Routine Description:
  24. This function unlinks a thread from the appropriate wait queues and sets
  25. the thread's wait completion status.
  26. Arguments:
  27. Thread - Supplies a pointer to a dispatcher object of type thread.
  28. WaitStatus - Supplies the wait completion status.
  29. Return Value:
  30. None.
  31. --*/
  32. {
  33. PKQUEUE Queue;
  34. PKTIMER Timer;
  35. PRKWAIT_BLOCK WaitBlock;
  36. //
  37. // Set wait completion status, remove wait blocks from object wait
  38. // lists, and remove thread from wait list.
  39. //
  40. Thread->WaitStatus |= WaitStatus;
  41. WaitBlock = Thread->WaitBlockList;
  42. do {
  43. RemoveEntryList(&WaitBlock->WaitListEntry);
  44. WaitBlock = WaitBlock->NextWaitBlock;
  45. } while (WaitBlock != Thread->WaitBlockList);
  46. if (Thread->WaitListEntry.Flink != NULL) {
  47. RemoveEntryList(&Thread->WaitListEntry);
  48. }
  49. //
  50. // If thread timer is still active, then cancel thread timer.
  51. //
  52. Timer = &Thread->Timer;
  53. if (Timer->Header.Inserted != FALSE) {
  54. KiRemoveTreeTimer(Timer);
  55. }
  56. //
  57. // If the thread is processing a queue entry, then increment the
  58. // count of currently active threads.
  59. //
  60. Queue = Thread->Queue;
  61. if (Queue != NULL) {
  62. Queue->CurrentCount += 1;
  63. }
  64. return;
  65. }
  66. VOID
  67. FASTCALL
  68. KiUnwaitThread (
  69. IN PRKTHREAD Thread,
  70. IN LONG_PTR WaitStatus,
  71. IN KPRIORITY Increment,
  72. IN PLIST_ENTRY ThreadList OPTIONAL
  73. )
  74. /*++
  75. Routine Description:
  76. This function unwaits a thread, sets the thread's wait completion status,
  77. calculates the thread's new priority, and either readies the thread for
  78. execution or adds the thread to a list of threads to be readied later.
  79. Arguments:
  80. Thread - Supplies a pointer to a dispatcher object of type thread.
  81. WaitStatus - Supplies the wait completion status.
  82. Increment - Supplies the priority increment that is to be applied to
  83. the thread's priority.
  84. ThreadList - Supplies an optional pointer to a listhead.
  85. Return Value:
  86. None.
  87. --*/
  88. {
  89. KPRIORITY NewPriority;
  90. PKPROCESS Process;
  91. //
  92. // Unlink thread from the appropriate wait queues and set the wait
  93. // completion status.
  94. //
  95. KiUnlinkThread(Thread, WaitStatus);
  96. //
  97. // If the thread runs at a realtime priority level, then reset the
  98. // thread quantum. Otherwise, compute the next thread priority and
  99. // charge the thread for the wait operation.
  100. //
  101. Process = Thread->ApcState.Process;
  102. if (Thread->Priority < LOW_REALTIME_PRIORITY) {
  103. if ((Thread->PriorityDecrement == 0) &&
  104. (Thread->DisableBoost == FALSE)) {
  105. NewPriority = Thread->BasePriority + Increment;
  106. //
  107. // If the specified thread is from a process with a foreground
  108. // memory priority, then add the foreground boost separation.
  109. //
  110. if (((PEPROCESS)Process)->Vm.Flags.MemoryPriority == MEMORY_PRIORITY_FOREGROUND) {
  111. NewPriority += PsPrioritySeperation;
  112. }
  113. //
  114. // If the new thread priority is greater than the current thread
  115. // priority, then boost the thread priority, but not above low
  116. // real time minus one.
  117. //
  118. if (NewPriority > Thread->Priority) {
  119. if (NewPriority >= LOW_REALTIME_PRIORITY) {
  120. NewPriority = LOW_REALTIME_PRIORITY - 1;
  121. }
  122. //
  123. // If the new thread priority is greater than the thread base
  124. // priority plus the specified increment (i.e., the foreground
  125. // separation was added), then set the priority decrement to
  126. // remove the separation boost after one quantum.
  127. //
  128. if (NewPriority > (Thread->BasePriority + Increment)) {
  129. Thread->PriorityDecrement =
  130. (SCHAR)(NewPriority - Thread->BasePriority - Increment);
  131. Thread->DecrementCount = ROUND_TRIP_DECREMENT_COUNT;
  132. }
  133. Thread->Priority = (SCHAR)NewPriority;
  134. }
  135. }
  136. if (Thread->BasePriority >= TIME_CRITICAL_PRIORITY_BOUND) {
  137. Thread->Quantum = Process->ThreadQuantum;
  138. } else {
  139. //
  140. // If the thread is being unwaited to execute a kernel APC,
  141. // then do not charge the thread any quantum. The wait code
  142. // will charge quantum after the kernel APC has executed and
  143. // the wait is actually satisifed.
  144. //
  145. if (WaitStatus != STATUS_KERNEL_APC) {
  146. Thread->Quantum -= WAIT_QUANTUM_DECREMENT;
  147. if (Thread->Quantum <= 0) {
  148. Thread->Quantum = Process->ThreadQuantum;
  149. Thread->Priority -= (Thread->PriorityDecrement + 1);
  150. if (Thread->Priority < Thread->BasePriority) {
  151. Thread->Priority = Thread->BasePriority;
  152. }
  153. Thread->PriorityDecrement = 0;
  154. }
  155. }
  156. }
  157. } else {
  158. Thread->Quantum = Process->ThreadQuantum;
  159. }
  160. //
  161. // If a thread list is specified, then add the thread to the enb of the
  162. // specified list. Otherwise, ready the thread for execution.
  163. //
  164. if (ARGUMENT_PRESENT(ThreadList)) {
  165. InsertTailList(ThreadList, &Thread->WaitListEntry);
  166. } else {
  167. KiReadyThread(Thread);
  168. }
  169. return;
  170. }
  171. VOID
  172. KeBoostCurrentThread(
  173. VOID
  174. )
  175. /*++
  176. Routine Description:
  177. This function boosts the priority of the current thread for one quantum,
  178. then reduce the thread priority to the base priority of the thread.
  179. Arguments:
  180. None.
  181. Return Value:
  182. None.
  183. --*/
  184. {
  185. KIRQL OldIrql;
  186. PKTHREAD Thread;
  187. //
  188. // Get current thread address, raise IRQL to synchronization level, and
  189. // lock the dispatcher database
  190. //
  191. Thread = KeGetCurrentThread();
  192. redoboost:
  193. KiLockDispatcherDatabase(&OldIrql);
  194. //
  195. // If a priority boost is not already active for the current thread
  196. // and the thread priority is less than 14, then boost the thread
  197. // priority to 14 and give the thread a large quantum. Otherwise,
  198. // if a priority boost is active, then decrement the round trip
  199. // count. If the count goes to zero, then release the dispatcher
  200. // database lock, lower the thread priority to the base priority,
  201. // and then attempt to boost the priority again. This will give
  202. // other threads a chance to run. If the count does not reach zero,
  203. // then give the thread another large qunatum.
  204. //
  205. // If the thread priority is above 14, then no boost is applied.
  206. //
  207. if ((Thread->PriorityDecrement == 0) && (Thread->Priority < 14)) {
  208. Thread->PriorityDecrement = 14 - Thread->BasePriority;
  209. Thread->DecrementCount = ROUND_TRIP_DECREMENT_COUNT;
  210. Thread->Priority = 14;
  211. Thread->Quantum = Thread->ApcState.Process->ThreadQuantum * 2;
  212. } else if (Thread->PriorityDecrement != 0) {
  213. Thread->DecrementCount -= 1;
  214. if (Thread->DecrementCount == 0) {
  215. KiUnlockDispatcherDatabase(OldIrql);
  216. KeSetPriorityThread(Thread, Thread->BasePriority);
  217. goto redoboost;
  218. } else {
  219. Thread->Quantum = Thread->ApcState.Process->ThreadQuantum * 2;
  220. }
  221. }
  222. KiUnlockDispatcherDatabase(OldIrql);
  223. return;
  224. }
  225. VOID
  226. FASTCALL
  227. KiWaitSatisfyAll (
  228. IN PRKWAIT_BLOCK WaitBlock
  229. )
  230. /*++
  231. Routine Description:
  232. This function satisfies a wait all and performs any side effects that
  233. are necessary.
  234. Arguments:
  235. WaitBlock - Supplies a pointer to a wait block.
  236. Return Value:
  237. None.
  238. --*/
  239. {
  240. PKMUTANT Object;
  241. PRKTHREAD Thread;
  242. PRKWAIT_BLOCK WaitBlock1;
  243. //
  244. // If the wait type was WaitAny, then perform neccessary side effects on
  245. // the object specified by the wait block. Else perform necessary side
  246. // effects on all the objects that were involved in the wait operation.
  247. //
  248. WaitBlock1 = WaitBlock;
  249. Thread = WaitBlock1->Thread;
  250. do {
  251. if (WaitBlock1->WaitKey != (CSHORT)STATUS_TIMEOUT) {
  252. Object = (PKMUTANT)WaitBlock1->Object;
  253. KiWaitSatisfyAny(Object, Thread);
  254. }
  255. WaitBlock1 = WaitBlock1->NextWaitBlock;
  256. } while (WaitBlock1 != WaitBlock);
  257. return;
  258. }
  259. VOID
  260. FASTCALL
  261. KiWaitTest (
  262. IN PVOID Object,
  263. IN KPRIORITY Increment
  264. )
  265. /*++
  266. Routine Description:
  267. This function tests if a wait can be satisfied when an object attains
  268. a state of signaled. If a wait can be satisfied, then the subject thread
  269. is unwaited with a completion status that is the WaitKey of the wait
  270. block from the object wait list. As many waits as possible are satisfied.
  271. Arguments:
  272. Object - Supplies a pointer to a dispatcher object.
  273. Return Value:
  274. None.
  275. --*/
  276. {
  277. PKEVENT Event;
  278. PLIST_ENTRY ListHead;
  279. PRKWAIT_BLOCK NextBlock;
  280. PKMUTANT Mutant;
  281. LIST_ENTRY ReadyList;
  282. PRKTHREAD Thread;
  283. PLIST_ENTRY ThreadEntry;
  284. PRKWAIT_BLOCK WaitBlock;
  285. PLIST_ENTRY WaitEntry;
  286. NTSTATUS WaitStatus;
  287. //
  288. // As long as the signal state of the specified object is Signaled and
  289. // there are waiters in the object wait list, then try to satisfy a wait.
  290. //
  291. Event = (PKEVENT)Object;
  292. ListHead = &Event->Header.WaitListHead;
  293. WaitEntry = ListHead->Flink;
  294. InitializeListHead(&ReadyList);
  295. while ((Event->Header.SignalState > 0) &&
  296. (WaitEntry != ListHead)) {
  297. WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
  298. Thread = WaitBlock->Thread;
  299. WaitStatus = STATUS_KERNEL_APC;
  300. //
  301. // N.B. The below code only satisfies the wait for wait any types.
  302. // Wait all types are satisfied in the wait code itself. This
  303. // is done with a eye to the future when the dispatcher lock is
  304. // split into a lock per waitable object type and a scheduling
  305. // state lock. For now, a kernel APC is simulated for wait all
  306. // types.
  307. //
  308. if (WaitBlock->WaitType == WaitAny) {
  309. WaitStatus = (NTSTATUS)WaitBlock->WaitKey;
  310. KiWaitSatisfyAny((PKMUTANT)Event, Thread);
  311. }
  312. KiUnwaitThread(Thread, WaitStatus, Increment, &ReadyList);
  313. WaitEntry = ListHead->Flink;
  314. }
  315. //
  316. // Ready any threads which have been made eligible to run. This
  317. // must be done AFTER the event is no longer needed to avoid running
  318. // the thread which owns the event before this routine is finished
  319. // looking at it.
  320. //
  321. while (!IsListEmpty(&ReadyList)) {
  322. ThreadEntry = RemoveHeadList(&ReadyList);
  323. Thread = CONTAINING_RECORD(ThreadEntry, KTHREAD, WaitListEntry);
  324. KiReadyThread(Thread);
  325. }
  326. return;
  327. }