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.

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