Leaked source code of windows server 2003
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.

774 lines
22 KiB

  1. /*++
  2. Copyright (c) 1993 Microsoft Corporation
  3. Module Name:
  4. queueobj.c
  5. Abstract:
  6. This module implements the kernel queue object. Functions are provided
  7. to initialize, read, insert, and remove queue objects.
  8. Author:
  9. David N. Cutler (davec) 31-Dec-1993
  10. Environment:
  11. Kernel mode only.
  12. --*/
  13. #include "ki.h"
  14. VOID
  15. KeInitializeQueue (
  16. IN PRKQUEUE Queue,
  17. IN ULONG Count OPTIONAL
  18. )
  19. /*++
  20. Routine Description:
  21. This function initializes a kernel queue object.
  22. Arguments:
  23. Queue - Supplies a pointer to a dispatcher object of type event.
  24. Count - Supplies the target maximum number of threads that should
  25. be concurrently active. If this parameter is not specified,
  26. then the number of processors is used.
  27. Return Value:
  28. None.
  29. --*/
  30. {
  31. //
  32. // Initialize standard dispatcher object header and set initial
  33. // state of queue object.
  34. //
  35. Queue->Header.Type = QueueObject;
  36. Queue->Header.Size = sizeof(KQUEUE) / sizeof(LONG);
  37. Queue->Header.SignalState = 0;
  38. InitializeListHead(&Queue->Header.WaitListHead);
  39. //
  40. // Initialize queue listhead, the thread list head, the current number
  41. // of threads, and the target maximum number of threads.
  42. //
  43. InitializeListHead(&Queue->EntryListHead);
  44. InitializeListHead(&Queue->ThreadListHead);
  45. Queue->CurrentCount = 0;
  46. if (ARGUMENT_PRESENT(Count)) {
  47. Queue->MaximumCount = Count;
  48. } else {
  49. Queue->MaximumCount = KeNumberProcessors;
  50. }
  51. return;
  52. }
  53. LONG
  54. KeReadStateQueue (
  55. IN PRKQUEUE Queue
  56. )
  57. /*++
  58. Routine Description:
  59. This function reads the current signal state of a Queue object.
  60. Arguments:
  61. Queue - Supplies a pointer to a dispatcher object of type Queue.
  62. Return Value:
  63. The current signal state of the Queue object.
  64. --*/
  65. {
  66. ASSERT_QUEUE(Queue);
  67. return Queue->Header.SignalState;
  68. }
  69. LONG
  70. KeInsertQueue (
  71. IN PRKQUEUE Queue,
  72. IN PLIST_ENTRY Entry
  73. )
  74. /*++
  75. Routine Description:
  76. This function inserts the specified entry in the queue object entry
  77. list and attempts to satisfy the wait of a single waiter.
  78. N.B. The wait discipline for Queue object is FIFO.
  79. Arguments:
  80. Queue - Supplies a pointer to a dispatcher object of type Queue.
  81. Entry - Supplies a pointer to a list entry that is inserted in the
  82. queue object entry list.
  83. Return Value:
  84. The previous signal state of the Queue object.
  85. --*/
  86. {
  87. KIRQL OldIrql;
  88. LONG OldState;
  89. ASSERT_QUEUE(Queue);
  90. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  91. //
  92. // Raise IRQL to SYNCH level and lock the dispatcher database.
  93. //
  94. KiLockDispatcherDatabase(&OldIrql);
  95. //
  96. // Insert the specified entry in the queue object entry list.
  97. //
  98. OldState = KiInsertQueue(Queue, Entry, FALSE);
  99. //
  100. // Unlock the dispatcher database, exit the dispatcher, and return the
  101. // signal state of queue object.
  102. //
  103. KiUnlockDispatcherDatabaseFromSynchLevel();
  104. KiExitDispatcher(OldIrql);
  105. return OldState;
  106. }
  107. LONG
  108. KeInsertHeadQueue (
  109. IN PRKQUEUE Queue,
  110. IN PLIST_ENTRY Entry
  111. )
  112. /*++
  113. Routine Description:
  114. This function inserts the specified entry in the queue object entry
  115. list and attempts to satisfy the wait of a single waiter.
  116. N.B. The wait discipline for Queue object is LIFO.
  117. Arguments:
  118. Queue - Supplies a pointer to a dispatcher object of type Queue.
  119. Entry - Supplies a pointer to a list entry that is inserted in the
  120. queue object entry list.
  121. Return Value:
  122. The previous signal state of the Queue object.
  123. --*/
  124. {
  125. KIRQL OldIrql;
  126. LONG OldState;
  127. ASSERT_QUEUE(Queue);
  128. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  129. //
  130. // Raise IRQL to SYNCH level and lock the dispatcher database.
  131. //
  132. KiLockDispatcherDatabase(&OldIrql);
  133. //
  134. // Insert the specified entry in the queue object entry list.
  135. //
  136. OldState = KiInsertQueue(Queue, Entry, TRUE);
  137. //
  138. // Unlock the dispatcher database, exit the dispatcher, and return the
  139. // signal state of queue object.
  140. //
  141. KiUnlockDispatcherDatabaseFromSynchLevel();
  142. KiExitDispatcher(OldIrql);
  143. return OldState;
  144. }
  145. //
  146. // The following macro initializes thread local variables for the wait
  147. // for single object kernel service while context switching is disabled.
  148. //
  149. // N.B. IRQL must be raised to DPC level prior to the invocation of this
  150. // macro.
  151. //
  152. // N.B. Initialization is done in this manner so this code does not get
  153. // executed inside the dispatcher lock.
  154. //
  155. #define InitializeRemoveQueue() \
  156. Thread->WaitBlockList = WaitBlock; \
  157. WaitBlock->Object = (PVOID)Queue; \
  158. WaitBlock->WaitKey = (CSHORT)(STATUS_SUCCESS); \
  159. WaitBlock->WaitType = WaitAny; \
  160. WaitBlock->Thread = Thread; \
  161. Thread->WaitStatus = 0; \
  162. if (ARGUMENT_PRESENT(Timeout)) { \
  163. WaitBlock->NextWaitBlock = WaitTimer; \
  164. WaitTimer->NextWaitBlock = WaitBlock; \
  165. Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry; \
  166. Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry; \
  167. } else { \
  168. WaitBlock->NextWaitBlock = WaitBlock; \
  169. } \
  170. Thread->Alertable = FALSE; \
  171. Thread->WaitMode = WaitMode; \
  172. Thread->WaitReason = WrQueue; \
  173. Thread->WaitListEntry.Flink = NULL; \
  174. StackSwappable = KiIsKernelStackSwappable(WaitMode, Thread); \
  175. Thread->WaitTime= KiQueryLowTickCount()
  176. PLIST_ENTRY
  177. KeRemoveQueue (
  178. IN PRKQUEUE Queue,
  179. IN KPROCESSOR_MODE WaitMode,
  180. IN PLARGE_INTEGER Timeout OPTIONAL
  181. )
  182. /*++
  183. Routine Description:
  184. This function removes the next entry from the Queue object entry
  185. list. If no list entry is available, then the calling thread is
  186. put in a wait state.
  187. N.B. The wait discipline for Queue object LIFO.
  188. Arguments:
  189. Queue - Supplies a pointer to a dispatcher object of type Queue.
  190. WaitMode - Supplies the processor mode in which the wait is to occur.
  191. Timeout - Supplies a pointer to an optional absolute of relative time over
  192. which the wait is to occur.
  193. Return Value:
  194. The address of the entry removed from the Queue object entry list or
  195. STATUS_TIMEOUT.
  196. N.B. These values can easily be distinguished by the fact that all
  197. addresses in kernel mode have the high order bit set.
  198. --*/
  199. {
  200. PKPRCB CurrentPrcb;
  201. LARGE_INTEGER DueTime;
  202. PLIST_ENTRY Entry;
  203. LARGE_INTEGER NewTime;
  204. PRKQUEUE OldQueue;
  205. PLARGE_INTEGER OriginalTime;
  206. LOGICAL StackSwappable;
  207. PRKTHREAD Thread;
  208. PRKTIMER Timer;
  209. PRKWAIT_BLOCK WaitBlock;
  210. LONG_PTR WaitStatus;
  211. PRKWAIT_BLOCK WaitTimer;
  212. ASSERT_QUEUE(Queue);
  213. //
  214. // Set constant variables.
  215. //
  216. OriginalTime = Timeout;
  217. Thread = KeGetCurrentThread();
  218. Timer = &Thread->Timer;
  219. WaitBlock = &Thread->WaitBlock[0];
  220. WaitTimer = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
  221. //
  222. // If the dispatcher database lock is already held, then initialize the
  223. // local variables. Otherwise, raise IRQL to SYNCH_LEVEL, initialize the
  224. // thread local variables, and lock the dispatcher database.
  225. //
  226. if (Thread->WaitNext) {
  227. Thread->WaitNext = FALSE;
  228. InitializeRemoveQueue();
  229. } else {
  230. Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
  231. InitializeRemoveQueue();
  232. KiLockDispatcherDatabaseAtSynchLevel();
  233. }
  234. //
  235. // Check if the thread is currently processing a queue entry and whether
  236. // the new queue is the same as the old queue.
  237. //
  238. OldQueue = Thread->Queue;
  239. Thread->Queue = Queue;
  240. if (Queue != OldQueue) {
  241. //
  242. // If the thread was previously associated with a queue, then remove
  243. // the thread from the old queue object thread list and attempt to
  244. // activate another thread.
  245. //
  246. Entry = &Thread->QueueListEntry;
  247. if (OldQueue != NULL) {
  248. RemoveEntryList(Entry);
  249. KiActivateWaiterQueue(OldQueue);
  250. }
  251. //
  252. // Insert thread in the thread list of the new queue that the thread
  253. // will be associate with.
  254. //
  255. InsertTailList(&Queue->ThreadListHead, Entry);
  256. } else {
  257. //
  258. // The previous and current queue are the same queue - decrement the
  259. // current number of threads.
  260. //
  261. Queue->CurrentCount -= 1;
  262. }
  263. //
  264. // Start of wait loop.
  265. //
  266. // Note this loop is repeated if a kernel APC is delivered in the
  267. // middle of the wait or a kernel APC is pending on the first attempt
  268. // through the loop.
  269. //
  270. // If the Queue object entry list is not empty, then remove the next
  271. // entry from the Queue object entry list. Otherwise, wait for an entry
  272. // to be inserted in the queue.
  273. //
  274. do {
  275. //
  276. // Check if there is a queue entry available and the current
  277. // number of active threads is less than target maximum number
  278. // of threads.
  279. //
  280. Entry = Queue->EntryListHead.Flink;
  281. if ((Entry != &Queue->EntryListHead) &&
  282. (Queue->CurrentCount < Queue->MaximumCount)) {
  283. //
  284. // Decrement the number of entires in the Queue object entry list,
  285. // increment the number of active threads, remove the next entry
  286. // from the list, and set the forward link to NULL.
  287. //
  288. Queue->Header.SignalState -= 1;
  289. Queue->CurrentCount += 1;
  290. if ((Entry->Flink == NULL) || (Entry->Blink == NULL)) {
  291. KeBugCheckEx(INVALID_WORK_QUEUE_ITEM,
  292. (ULONG_PTR)Entry,
  293. (ULONG_PTR)Queue,
  294. (ULONG_PTR)&ExWorkerQueue[0],
  295. (ULONG_PTR)((PWORK_QUEUE_ITEM)Entry)->WorkerRoutine);
  296. }
  297. RemoveEntryList(Entry);
  298. Entry->Flink = NULL;
  299. break;
  300. } else {
  301. //
  302. // Test to determine if a kernel APC is pending.
  303. //
  304. // If a kernel APC is pending, the special APC disable count is
  305. // zero, and the previous IRQL was less than APC_LEVEL, then a
  306. // kernel APC was queued by another processor just after IRQL was
  307. // raised to DISPATCH_LEVEL, but before the dispatcher database
  308. // was locked.
  309. //
  310. // N.B. that this can only happen in a multiprocessor system.
  311. //
  312. if (Thread->ApcState.KernelApcPending &&
  313. (Thread->SpecialApcDisable == 0) &&
  314. (Thread->WaitIrql < APC_LEVEL)) {
  315. //
  316. // Increment the current thread count, unlock the dispatcher
  317. // database, and exit the dispatcher. An APC interrupt will
  318. // immediately occur which will result in the delivery of the
  319. // kernel APC, if possible.
  320. //
  321. Queue->CurrentCount += 1;
  322. KiRequestSoftwareInterrupt(APC_LEVEL);
  323. KiUnlockDispatcherDatabaseFromSynchLevel();
  324. KiExitDispatcher(Thread->WaitIrql);
  325. } else {
  326. //
  327. // Test if a user APC is pending.
  328. //
  329. if ((WaitMode != KernelMode) && (Thread->ApcState.UserApcPending)) {
  330. Entry = (PLIST_ENTRY)ULongToPtr(STATUS_USER_APC);
  331. Queue->CurrentCount += 1;
  332. break;
  333. }
  334. //
  335. // Check to determine if a timeout value is specified.
  336. //
  337. if (ARGUMENT_PRESENT(Timeout)) {
  338. //
  339. // If the timeout value is zero, then return immediately
  340. // without waiting.
  341. //
  342. if (!(Timeout->LowPart | Timeout->HighPart)) {
  343. Entry = (PLIST_ENTRY)ULongToPtr(STATUS_TIMEOUT);
  344. Queue->CurrentCount += 1;
  345. break;
  346. }
  347. //
  348. // Insert the timer in the timer tree.
  349. //
  350. // N.B. The constant fields of the timer wait block are
  351. // initialized when the thread is initialized. The
  352. // constant fields include the wait object, wait key,
  353. // wait type, and the wait list entry link pointers.
  354. //
  355. if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
  356. Entry = (PLIST_ENTRY)ULongToPtr(STATUS_TIMEOUT);
  357. Queue->CurrentCount += 1;
  358. break;
  359. }
  360. DueTime.QuadPart = Timer->DueTime.QuadPart;
  361. }
  362. //
  363. // Insert wait block in object wait list.
  364. //
  365. InsertTailList(&Queue->Header.WaitListHead, &WaitBlock->WaitListEntry);
  366. //
  367. // Set the thread wait parameters, set the thread dispatcher
  368. // state to Waiting, and insert the thread in the wait list.
  369. //
  370. CurrentPrcb = KeGetCurrentPrcb();
  371. Thread->State = Waiting;
  372. if (StackSwappable != FALSE) {
  373. InsertTailList(&CurrentPrcb->WaitListHead, &Thread->WaitListEntry);
  374. }
  375. //
  376. // Set swap busy for the current thread, unlock the dispatcher
  377. // database, and switch to a new thread.
  378. //
  379. // Control is returned at the original IRQL.
  380. //
  381. ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
  382. KiSetContextSwapBusy(Thread);
  383. KiUnlockDispatcherDatabaseFromSynchLevel();
  384. WaitStatus = KiSwapThread(Thread, CurrentPrcb);
  385. //
  386. // If the thread was not awakened to deliver a kernel mode APC,
  387. // then return wait status.
  388. //
  389. Thread->WaitReason = 0;
  390. if (WaitStatus != STATUS_KERNEL_APC) {
  391. return (PLIST_ENTRY)WaitStatus;
  392. }
  393. if (ARGUMENT_PRESENT(Timeout)) {
  394. //
  395. // Reduce the amount of time remaining before timeout occurs.
  396. //
  397. Timeout = KiComputeWaitInterval(OriginalTime,
  398. &DueTime,
  399. &NewTime);
  400. }
  401. }
  402. //
  403. // Raise IRQL to SYNCH level, initialize the local variables,
  404. // lock the dispatcher database, and decrement the count of
  405. // active threads.
  406. //
  407. Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
  408. InitializeRemoveQueue();
  409. KiLockDispatcherDatabaseAtSynchLevel();
  410. Queue->CurrentCount -= 1;
  411. }
  412. } while (TRUE);
  413. //
  414. // Unlock the dispatcher database, exit the dispatcher, and return the
  415. // list entry address or a status of timeout.
  416. //
  417. KiUnlockDispatcherDatabaseFromSynchLevel();
  418. KiExitDispatcher(Thread->WaitIrql);
  419. return Entry;
  420. }
  421. PLIST_ENTRY
  422. KeRundownQueue (
  423. IN PRKQUEUE Queue
  424. )
  425. /*++
  426. Routine Description:
  427. This function runs down the specified queue by removing the listhead
  428. from the queue list, removing any associated threads from the thread
  429. list, and returning the address of the first entry.
  430. Arguments:
  431. Queue - Supplies a pointer to a dispatcher object of type Queue.
  432. Return Value:
  433. If the queue list is not empty, then the address of the first entry in
  434. the queue is returned as the function value. Otherwise, a value of NULL
  435. is returned.
  436. --*/
  437. {
  438. PLIST_ENTRY Entry;
  439. PLIST_ENTRY FirstEntry;
  440. KIRQL OldIrql;
  441. PKTHREAD Thread;
  442. ASSERT_QUEUE(Queue);
  443. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  444. ASSERT(IsListEmpty(&Queue->Header.WaitListHead));
  445. //
  446. // Raise IRQL to SYNCH level and lock the dispatcher database.
  447. //
  448. KiLockDispatcherDatabase(&OldIrql);
  449. //
  450. // Get the address of the first entry in the queue and check if the
  451. // list is empty or contains entries that should be flushed. If there
  452. // are no entries in the list, then set the return value to NULL.
  453. // Otherwise, set the return value to the address of the first list
  454. // entry and remove the listhead from the list.
  455. //
  456. FirstEntry = Queue->EntryListHead.Flink;
  457. if (FirstEntry == &Queue->EntryListHead) {
  458. FirstEntry = NULL;
  459. } else {
  460. RemoveEntryList(&Queue->EntryListHead);
  461. }
  462. //
  463. // Remove all associated threads from the thread list of the queue.
  464. //
  465. while (Queue->ThreadListHead.Flink != &Queue->ThreadListHead) {
  466. Entry = Queue->ThreadListHead.Flink;
  467. Thread = CONTAINING_RECORD(Entry, KTHREAD, QueueListEntry);
  468. Thread->Queue = NULL;
  469. RemoveEntryList(Entry);
  470. }
  471. #if DBG
  472. Queue->EntryListHead.Flink = Queue->EntryListHead.Blink = NULL;
  473. Queue->ThreadListHead.Flink = Queue->ThreadListHead.Blink = NULL;
  474. Queue->Header.WaitListHead.Flink = Queue->Header.WaitListHead.Blink = NULL;
  475. #endif
  476. //
  477. // Unlock the dispatcher database, exit the dispatcher, and return the
  478. // function value.
  479. //
  480. KiUnlockDispatcherDatabaseFromSynchLevel();
  481. KiExitDispatcher(OldIrql);
  482. return FirstEntry;
  483. }
  484. LONG
  485. FASTCALL
  486. KiInsertQueue (
  487. IN PRKQUEUE Queue,
  488. IN PLIST_ENTRY Entry,
  489. IN BOOLEAN Head
  490. )
  491. /*++
  492. Routine Description:
  493. This function inserts the specified entry in the queue object entry
  494. list and attempts to satisfy the wait of a single waiter.
  495. N.B. The wait discipline for Queue object is LIFO.
  496. Arguments:
  497. Queue - Supplies a pointer to a dispatcher object of type Queue.
  498. Entry - Supplies a pointer to a list entry that is inserted in the
  499. queue object entry list.
  500. Head - Supplies a boolean value that determines whether the queue
  501. entry is inserted at the head or tail of the queue if it can
  502. not be immediately dispatched.
  503. Return Value:
  504. The previous signal state of the Queue object.
  505. --*/
  506. {
  507. LONG OldState;
  508. PRKTHREAD Thread;
  509. PKTIMER Timer;
  510. PKWAIT_BLOCK WaitBlock;
  511. PLIST_ENTRY WaitEntry;
  512. ASSERT_QUEUE(Queue);
  513. //
  514. // Capture the current signal state of queue object and check if there
  515. // is a thread waiting on the queue object, the current number of active
  516. // threads is less than the target number of threads, and the wait reason
  517. // of the current thread is not queue wait or the wait queue is not the
  518. // same queue as the insertion queue. If these conditions are satisfied,
  519. // then satisfy the thread wait and pass the thread the address of the
  520. // queue entry as the wait status. Otherwise, set the state of the queue
  521. // object to signaled and insert the specified entry in the queue object
  522. // entry list.
  523. //
  524. OldState = Queue->Header.SignalState;
  525. Thread = KeGetCurrentThread();
  526. WaitEntry = Queue->Header.WaitListHead.Blink;
  527. if ((WaitEntry != &Queue->Header.WaitListHead) &&
  528. (Queue->CurrentCount < Queue->MaximumCount) &&
  529. ((Thread->Queue != Queue) ||
  530. (Thread->WaitReason != WrQueue))) {
  531. //
  532. // Remove the last wait block from the wait list and get the address
  533. // of the waiting thread object.
  534. //
  535. RemoveEntryList(WaitEntry);
  536. WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
  537. Thread = WaitBlock->Thread;
  538. //
  539. // Set the wait completion status, remove the thread from its wait
  540. // list, increment the number of active threads, and clear the wait
  541. // reason.
  542. //
  543. Thread->WaitStatus = (LONG_PTR)Entry;
  544. if (Thread->WaitListEntry.Flink != NULL) {
  545. RemoveEntryList(&Thread->WaitListEntry);
  546. }
  547. Queue->CurrentCount += 1;
  548. Thread->WaitReason = 0;
  549. //
  550. // If thread timer is still active, then cancel thread timer.
  551. //
  552. Timer = &Thread->Timer;
  553. if (Timer->Header.Inserted == TRUE) {
  554. KiRemoveTreeTimer(Timer);
  555. }
  556. //
  557. // Ready the thread for execution.
  558. //
  559. KiReadyThread(Thread);
  560. } else {
  561. Queue->Header.SignalState += 1;
  562. if (Head != FALSE) {
  563. InsertHeadList(&Queue->EntryListHead, Entry);
  564. } else {
  565. InsertTailList(&Queue->EntryListHead, Entry);
  566. }
  567. }
  568. return OldState;
  569. }