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.

1166 lines
26 KiB

  1. /*++
  2. Copyright (c) 1998-2001 Microsoft Corporation
  3. Module Name:
  4. thrdpool.cxx
  5. Abstract:
  6. This module implements the thread pool package.
  7. Author:
  8. Keith Moore (keithmo) 10-Jun-1998
  9. Revision History:
  10. --*/
  11. #include <precomp.h>
  12. #ifdef __cplusplus
  13. extern "C" {
  14. #endif // __cplusplus
  15. //
  16. // Private prototypes.
  17. //
  18. NTSTATUS
  19. UlpCreatePoolThread(
  20. IN PUL_THREAD_POOL pThreadPool
  21. );
  22. VOID
  23. UlpThreadPoolWorker(
  24. IN PVOID Context
  25. );
  26. VOID
  27. UlpInitThreadTracker(
  28. IN PUL_THREAD_POOL pThreadPool,
  29. IN PETHREAD pThread,
  30. IN PUL_THREAD_TRACKER pThreadTracker
  31. );
  32. VOID
  33. UlpDestroyThreadTracker(
  34. IN PUL_THREAD_TRACKER pThreadTracker
  35. );
  36. PUL_THREAD_TRACKER
  37. UlpPopThreadTracker(
  38. IN PUL_THREAD_POOL pThreadPool
  39. );
  40. VOID
  41. UlpKillThreadWorker(
  42. IN PUL_WORK_ITEM pWorkItem
  43. );
  44. #ifdef __cplusplus
  45. }; // extern "C"
  46. #endif // __cplusplus
  47. //
  48. // Private globals.
  49. //
  50. DECLSPEC_ALIGN(UL_CACHE_LINE)
  51. UL_ALIGNED_THREAD_POOL g_UlThreadPool[MAXIMUM_PROCESSORS + 1];
  52. #define CURRENT_THREAD_POOL() \
  53. &g_UlThreadPool[KeGetCurrentProcessorNumber()].ThreadPool
  54. #define WAIT_THREAD_POOL() \
  55. &g_UlThreadPool[g_UlNumberOfProcessors].ThreadPool
  56. PUL_WORK_ITEM g_pKillerWorkItems = NULL;
  57. #ifdef ALLOC_PRAGMA
  58. #pragma alloc_text( PAGE, UlInitializeThreadPool )
  59. #pragma alloc_text( PAGE, UlTerminateThreadPool )
  60. #pragma alloc_text( PAGE, UlpCreatePoolThread )
  61. #pragma alloc_text( PAGE, UlpThreadPoolWorker )
  62. #endif // ALLOC_PRAGMA
  63. #if 0
  64. NOT PAGEABLE -- UlpInitThreadTracker
  65. NOT PAGEABLE -- UlpDestroyThreadTracker
  66. NOT PAGEABLE -- UlpPopThreadTracker
  67. #endif
  68. //
  69. // Public functions.
  70. //
  71. /***************************************************************************++
  72. Routine Description:
  73. Initialize the thread pool.
  74. Arguments:
  75. ThreadsPerCpu - Supplies the number of threads to create per CPU.
  76. Return Value:
  77. NTSTATUS - Completion status.
  78. --***************************************************************************/
  79. NTSTATUS
  80. UlInitializeThreadPool(
  81. IN USHORT ThreadsPerCpu
  82. )
  83. {
  84. NTSTATUS Status;
  85. PUL_THREAD_POOL pThreadPool;
  86. CLONG i;
  87. USHORT j;
  88. //
  89. // Sanity check.
  90. //
  91. PAGED_CODE();
  92. RtlZeroMemory( g_UlThreadPool, sizeof(g_UlThreadPool) );
  93. //
  94. // Preallocate the small array of special work items used by
  95. // UlTerminateThreadPool, so that we can safely shut down even
  96. // in low-memory conditions
  97. //
  98. g_pKillerWorkItems = UL_ALLOCATE_ARRAY(
  99. NonPagedPool,
  100. UL_WORK_ITEM,
  101. (g_UlNumberOfProcessors + 1) * ThreadsPerCpu,
  102. UL_WORK_ITEM_POOL_TAG
  103. );
  104. if (g_pKillerWorkItems == NULL)
  105. {
  106. return STATUS_INSUFFICIENT_RESOURCES;
  107. }
  108. for (i = 0; i <= g_UlNumberOfProcessors; i++)
  109. {
  110. pThreadPool = &g_UlThreadPool[i].ThreadPool;
  111. //
  112. // Initialize the kernel structures.
  113. //
  114. InitializeSListHead( &pThreadPool->WorkQueueSList );
  115. KeInitializeEvent(
  116. &pThreadPool->WorkQueueEvent,
  117. SynchronizationEvent,
  118. FALSE
  119. );
  120. UlInitializeSpinLock( &pThreadPool->ThreadSpinLock, "ThreadSpinLock" );
  121. //
  122. // Initialize the other fields.
  123. //
  124. pThreadPool->pIrpThread = NULL;
  125. pThreadPool->ThreadCount = 0;
  126. pThreadPool->ThreadCpu = (UCHAR)i;
  127. InitializeListHead( &pThreadPool->ThreadListHead );
  128. }
  129. for (i = 0; i <= g_UlNumberOfProcessors; i++)
  130. {
  131. pThreadPool = &g_UlThreadPool[i].ThreadPool;
  132. //
  133. // Create the threads.
  134. //
  135. for (j = 0; j < ThreadsPerCpu; j++)
  136. {
  137. Status = UlpCreatePoolThread( pThreadPool );
  138. if (NT_SUCCESS(Status))
  139. {
  140. pThreadPool->Initialized = TRUE;
  141. pThreadPool->ThreadCount++;
  142. }
  143. else
  144. {
  145. break;
  146. }
  147. }
  148. if (!NT_SUCCESS(Status))
  149. {
  150. break;
  151. }
  152. }
  153. return Status;
  154. } // UlInitializeThreadPool
  155. /***************************************************************************++
  156. Routine Description:
  157. Terminates the thread pool, waiting for all worker threads to exit.
  158. --***************************************************************************/
  159. VOID
  160. UlTerminateThreadPool(
  161. VOID
  162. )
  163. {
  164. PUL_THREAD_POOL pThreadPool;
  165. PUL_THREAD_TRACKER pThreadTracker;
  166. CLONG i, j;
  167. PUL_WORK_ITEM pKiller = g_pKillerWorkItems;
  168. //
  169. // Sanity check.
  170. //
  171. PAGED_CODE();
  172. //
  173. // If there is no killer, the thread pool has never been initialized.
  174. //
  175. if (pKiller == NULL)
  176. {
  177. return;
  178. }
  179. for (i = 0; i <= g_UlNumberOfProcessors; i++)
  180. {
  181. pThreadPool = &g_UlThreadPool[i].ThreadPool;
  182. if (pThreadPool->Initialized)
  183. {
  184. //
  185. // Queue a killer work item for each thread. Each
  186. // killer tells one thread to kill itself.
  187. //
  188. for (j = 0; j < pThreadPool->ThreadCount; j++)
  189. {
  190. //
  191. // Need a separate work item for each thread.
  192. // Worker threads will free the below memory
  193. // before termination. UlpKillThreadWorker is
  194. // a sign to a worker thread for self termination.
  195. //
  196. pKiller->pWorkRoutine = &UlpKillThreadWorker;
  197. QUEUE_UL_WORK_ITEM( pThreadPool, pKiller );
  198. pKiller++;
  199. }
  200. //
  201. // Wait for all threads to go away.
  202. //
  203. while (pThreadTracker = UlpPopThreadTracker(pThreadPool))
  204. {
  205. UlpDestroyThreadTracker(pThreadTracker);
  206. }
  207. //
  208. // Release the thread handle.
  209. //
  210. ZwClose( pThreadPool->ThreadHandle );
  211. }
  212. ASSERT( IsListEmpty( &pThreadPool->ThreadListHead ) );
  213. }
  214. UL_FREE_POOL(g_pKillerWorkItems, UL_WORK_ITEM_POOL_TAG);
  215. g_pKillerWorkItems = NULL;
  216. } // UlTerminateThreadPool
  217. //
  218. // Private functions.
  219. //
  220. /***************************************************************************++
  221. Routine Description:
  222. Creates a new pool thread, setting pIrpThread if necessary.
  223. Arguments:
  224. pThreadPool - Supplies the pool that is to receive the new thread.
  225. Return Value:
  226. NTSTATUS - Completion status.
  227. --***************************************************************************/
  228. NTSTATUS
  229. UlpCreatePoolThread(
  230. IN PUL_THREAD_POOL pThreadPool
  231. )
  232. {
  233. NTSTATUS Status;
  234. OBJECT_ATTRIBUTES ObjectAttributes;
  235. PUL_THREAD_TRACKER pThreadTracker;
  236. PETHREAD pThread;
  237. //
  238. // Sanity check.
  239. //
  240. PAGED_CODE();
  241. //
  242. // Ensure we can allocate a thread tracker.
  243. //
  244. pThreadTracker = (PUL_THREAD_TRACKER) UL_ALLOCATE_POOL(
  245. NonPagedPool,
  246. sizeof(*pThreadTracker),
  247. UL_THREAD_TRACKER_POOL_TAG
  248. );
  249. if (pThreadTracker != NULL)
  250. {
  251. //
  252. // Create the thread.
  253. //
  254. InitializeObjectAttributes(
  255. &ObjectAttributes, // ObjectAttributes
  256. NULL, // ObjectName
  257. UL_KERNEL_HANDLE, // Attributes
  258. NULL, // RootDirectory
  259. NULL // SecurityDescriptor
  260. );
  261. UlAttachToSystemProcess();
  262. Status = PsCreateSystemThread(
  263. &pThreadPool->ThreadHandle, // ThreadHandle
  264. THREAD_ALL_ACCESS, // DesiredAccess
  265. &ObjectAttributes, // ObjectAttributes
  266. NULL, // ProcessHandle
  267. NULL, // ClientId
  268. UlpThreadPoolWorker, // StartRoutine
  269. pThreadPool // StartContext
  270. );
  271. if (NT_SUCCESS(Status))
  272. {
  273. //
  274. // Get a pointer to the thread.
  275. //
  276. Status = ObReferenceObjectByHandle(
  277. pThreadPool->ThreadHandle, // ThreadHandle
  278. 0, // DesiredAccess
  279. *PsThreadType, // ObjectType
  280. KernelMode, // AccessMode
  281. (PVOID*) &pThread, // Object
  282. NULL // HandleInformation
  283. );
  284. if (NT_SUCCESS(Status))
  285. {
  286. //
  287. // Set up the thread tracker.
  288. //
  289. UlpInitThreadTracker(pThreadPool, pThread, pThreadTracker);
  290. //
  291. // If this is the first thread created for this pool,
  292. // make it into the special IRP thread.
  293. //
  294. if (pThreadPool->pIrpThread == NULL)
  295. {
  296. pThreadPool->pIrpThread = pThread;
  297. }
  298. }
  299. else
  300. {
  301. //
  302. // That call really should not fail.
  303. //
  304. ASSERT(NT_SUCCESS(Status));
  305. UL_FREE_POOL(
  306. pThreadTracker,
  307. UL_THREAD_TRACKER_POOL_TAG
  308. );
  309. //
  310. // Preserve return val from ObReferenceObjectByHandle.
  311. //
  312. ZwClose( pThreadPool->ThreadHandle );
  313. }
  314. }
  315. else
  316. {
  317. //
  318. // Couldn't create the thread, kill the tracker.
  319. //
  320. UL_FREE_POOL(
  321. pThreadTracker,
  322. UL_THREAD_TRACKER_POOL_TAG
  323. );
  324. }
  325. UlDetachFromSystemProcess();
  326. }
  327. else
  328. {
  329. //
  330. // Couldn't create a thread tracker.
  331. //
  332. Status = STATUS_INSUFFICIENT_RESOURCES;
  333. }
  334. return Status;
  335. } // UlpCreatePoolThread
  336. /***************************************************************************++
  337. Routine Description:
  338. This is the main worker for pool threads.
  339. Arguments:
  340. pContext - Supplies a context value for the thread. This is actually
  341. a PUL_THREAD_POOL pointer.
  342. --***************************************************************************/
  343. VOID
  344. UlpThreadPoolWorker(
  345. IN PVOID pContext
  346. )
  347. {
  348. PSINGLE_LIST_ENTRY pListEntry;
  349. PSINGLE_LIST_ENTRY pNext;
  350. SINGLE_LIST_ENTRY ListHead;
  351. PUL_WORK_ROUTINE pWorkRoutine;
  352. PUL_THREAD_POOL pThreadPool;
  353. PUL_THREAD_POOL pThreadPoolNext;
  354. PUL_WORK_ITEM pWorkItem;
  355. KAFFINITY AffinityMask;
  356. NTSTATUS Status;
  357. ULONG Cpu;
  358. ULONG NextCpu;
  359. ULONG ThreadCpu;
  360. //
  361. // Sanity check.
  362. //
  363. PAGED_CODE();
  364. //
  365. // Initialize the ListHead to reverse order of items from FlushSList.
  366. //
  367. ListHead.Next = NULL;
  368. //
  369. // Snag the context.
  370. //
  371. pThreadPool = (PUL_THREAD_POOL) pContext;
  372. //
  373. // Set this thread's hard affinity if enabled plus ideal processor.
  374. //
  375. if ( pThreadPool->ThreadCpu != g_UlNumberOfProcessors )
  376. {
  377. if ( g_UlEnableThreadAffinity )
  378. {
  379. AffinityMask = 1 << pThreadPool->ThreadCpu;
  380. }
  381. else
  382. {
  383. AffinityMask = (KAFFINITY) g_UlThreadAffinityMask;
  384. }
  385. Status = ZwSetInformationThread(
  386. pThreadPool->ThreadHandle,
  387. ThreadAffinityMask,
  388. &AffinityMask,
  389. sizeof(AffinityMask)
  390. );
  391. ASSERT( NT_SUCCESS( Status ) );
  392. //
  393. // Always set thread's ideal processor.
  394. //
  395. ThreadCpu = pThreadPool->ThreadCpu;
  396. Status = ZwSetInformationThread(
  397. pThreadPool->ThreadHandle,
  398. ThreadIdealProcessor,
  399. &ThreadCpu,
  400. sizeof(ThreadCpu)
  401. );
  402. ASSERT( NT_SUCCESS( Status ) );
  403. }
  404. //
  405. // Disable hard error popups.
  406. //
  407. IoSetThreadHardErrorMode( FALSE );
  408. //
  409. // Loop forever, or at least until we're told to stop.
  410. //
  411. while ( TRUE )
  412. {
  413. //
  414. // Dqueue the next work item. If we get the special killer work
  415. // item, then break out of this loop & handle it.
  416. //
  417. pListEntry = InterlockedFlushSList( &pThreadPool->WorkQueueSList );
  418. if ( NULL == pListEntry )
  419. {
  420. //
  421. // Try to get a work from other queues.
  422. //
  423. NextCpu = pThreadPool->ThreadCpu + 1;
  424. for (Cpu = 0; Cpu < g_UlNumberOfProcessors; Cpu++, NextCpu++)
  425. {
  426. if (NextCpu >= g_UlNumberOfProcessors)
  427. {
  428. NextCpu = 0;
  429. }
  430. pThreadPoolNext = &g_UlThreadPool[NextCpu].ThreadPool;
  431. if ( ExQueryDepthSList( &pThreadPoolNext->WorkQueueSList ) >=
  432. g_UlMinWorkDequeueDepth )
  433. {
  434. pListEntry = InterlockedPopEntrySList(
  435. &pThreadPoolNext->WorkQueueSList
  436. );
  437. if ( NULL != pListEntry )
  438. {
  439. //
  440. // Make sure we didn't pop up a killer. If so,
  441. // push it back to where it is poped from.
  442. //
  443. pWorkItem = CONTAINING_RECORD(
  444. pListEntry,
  445. UL_WORK_ITEM,
  446. QueueListEntry
  447. );
  448. if ( pWorkItem->pWorkRoutine != &UlpKillThreadWorker )
  449. {
  450. pListEntry->Next = NULL;
  451. }
  452. else
  453. {
  454. WRITE_REF_TRACE_LOG(
  455. g_pWorkItemTraceLog,
  456. REF_ACTION_PUSH_BACK_WORK_ITEM,
  457. 0,
  458. pWorkItem,
  459. __FILE__,
  460. __LINE__
  461. );
  462. QUEUE_UL_WORK_ITEM( pThreadPoolNext, pWorkItem );
  463. pListEntry = NULL;
  464. }
  465. break;
  466. }
  467. }
  468. }
  469. }
  470. if ( NULL == pListEntry )
  471. {
  472. KeWaitForSingleObject(
  473. &pThreadPool->WorkQueueEvent,
  474. Executive,
  475. KernelMode,
  476. FALSE,
  477. 0
  478. );
  479. continue;
  480. }
  481. ASSERT( NULL != pListEntry );
  482. //
  483. // Rebuild the list with reverse order of what we received.
  484. //
  485. while ( pListEntry != NULL )
  486. {
  487. WRITE_REF_TRACE_LOG(
  488. g_pWorkItemTraceLog,
  489. REF_ACTION_FLUSH_WORK_ITEM,
  490. 0,
  491. pListEntry,
  492. __FILE__,
  493. __LINE__
  494. );
  495. pNext = pListEntry->Next;
  496. pListEntry->Next = ListHead.Next;
  497. ListHead.Next = pListEntry;
  498. pListEntry = pNext;
  499. }
  500. //
  501. // We can now process the work items in the order that was received.
  502. //
  503. while ( NULL != ( pListEntry = ListHead.Next ) )
  504. {
  505. ListHead.Next = pListEntry->Next;
  506. pWorkItem = CONTAINING_RECORD(
  507. pListEntry,
  508. UL_WORK_ITEM,
  509. QueueListEntry
  510. );
  511. WRITE_REF_TRACE_LOG(
  512. g_pWorkItemTraceLog,
  513. REF_ACTION_PROCESS_WORK_ITEM,
  514. 0,
  515. pWorkItem,
  516. __FILE__,
  517. __LINE__
  518. );
  519. //
  520. // Call the actual work item routine.
  521. //
  522. ASSERT( pWorkItem->pWorkRoutine != NULL );
  523. if ( pWorkItem->pWorkRoutine == &UlpKillThreadWorker )
  524. {
  525. //
  526. // Received a special signal for self-termination.
  527. // Push all remaining work items back to the queue
  528. // before we exit the current thread.
  529. //
  530. while ( NULL != ( pListEntry = ListHead.Next ) )
  531. {
  532. ListHead.Next = pListEntry->Next;
  533. pWorkItem = CONTAINING_RECORD(
  534. pListEntry,
  535. UL_WORK_ITEM,
  536. QueueListEntry
  537. );
  538. ASSERT( pWorkItem->pWorkRoutine == &UlpKillThreadWorker );
  539. WRITE_REF_TRACE_LOG(
  540. g_pWorkItemTraceLog,
  541. REF_ACTION_PUSH_BACK_WORK_ITEM,
  542. 0,
  543. pWorkItem,
  544. __FILE__,
  545. __LINE__
  546. );
  547. QUEUE_UL_WORK_ITEM( pThreadPool, pWorkItem );
  548. }
  549. goto exit;
  550. }
  551. UL_ENTER_DRIVER( "UlpThreadPoolWorker", NULL );
  552. //
  553. // Clear the pWorkRoutine member as an indication that this
  554. // has started processing. Must do it before calling the
  555. // routine, as the routine may destroy the work item.
  556. //
  557. pWorkRoutine = pWorkItem->pWorkRoutine;
  558. pWorkItem->pWorkRoutine = NULL;
  559. pWorkRoutine( pWorkItem );
  560. UL_LEAVE_DRIVER( "UlpThreadPoolWorker" );
  561. }
  562. }
  563. exit:
  564. //
  565. // Suicide is painless.
  566. //
  567. PsTerminateSystemThread( STATUS_SUCCESS );
  568. } // UlpThreadPoolWorker
  569. /***************************************************************************++
  570. Routine Description:
  571. Initializes a new thread tracker and inserts it into the thread pool.
  572. Arguments:
  573. pThreadPool - Supplies the thread pool to own the new tracker.
  574. pThread - Supplies the thread for the tracker.
  575. pThreadTracker - Supplise the tracker to be initialized
  576. --***************************************************************************/
  577. VOID
  578. UlpInitThreadTracker(
  579. IN PUL_THREAD_POOL pThreadPool,
  580. IN PETHREAD pThread,
  581. IN PUL_THREAD_TRACKER pThreadTracker
  582. )
  583. {
  584. KIRQL oldIrql;
  585. ASSERT( pThreadPool != NULL );
  586. ASSERT( pThread != NULL );
  587. ASSERT( pThreadTracker != NULL );
  588. pThreadTracker->pThread = pThread;
  589. UlAcquireSpinLock( &pThreadPool->ThreadSpinLock, &oldIrql );
  590. InsertTailList(
  591. &pThreadPool->ThreadListHead,
  592. &pThreadTracker->ThreadListEntry
  593. );
  594. UlReleaseSpinLock( &pThreadPool->ThreadSpinLock, oldIrql );
  595. } // UlpInitThreadTracker
  596. /***************************************************************************++
  597. Routine Description:
  598. Removes the specified thread tracker from the thread pool.
  599. Arguments:
  600. pThreadPool - Supplies the thread pool that owns the tracker.
  601. pThreadTracker - Supplies the thread tracker to remove.
  602. Return Value:
  603. None
  604. --***************************************************************************/
  605. VOID
  606. UlpDestroyThreadTracker(
  607. IN PUL_THREAD_TRACKER pThreadTracker
  608. )
  609. {
  610. KIRQL oldIrql;
  611. //
  612. // Sanity check.
  613. //
  614. ASSERT( pThreadTracker != NULL );
  615. //
  616. // Wait for the thread to die.
  617. //
  618. KeWaitForSingleObject(
  619. (PVOID)pThreadTracker->pThread, // Object
  620. UserRequest, // WaitReason
  621. KernelMode, // WaitMode
  622. FALSE, // Alertable
  623. NULL // Timeout
  624. );
  625. //
  626. // Cleanup.
  627. //
  628. ObDereferenceObject( pThreadTracker->pThread );
  629. //
  630. // Do it.
  631. //
  632. UL_FREE_POOL(
  633. pThreadTracker,
  634. UL_THREAD_TRACKER_POOL_TAG
  635. );
  636. } // UlpDestroyThreadTracker
  637. /***************************************************************************++
  638. Routine Description:
  639. Removes a thread tracker from the thread pool.
  640. Arguments:
  641. pThreadPool - Supplies the thread pool that owns the tracker.
  642. Return Value:
  643. A pointer to the tracker or NULL (if list is empty)
  644. --***************************************************************************/
  645. PUL_THREAD_TRACKER
  646. UlpPopThreadTracker(
  647. IN PUL_THREAD_POOL pThreadPool
  648. )
  649. {
  650. PLIST_ENTRY pEntry;
  651. PUL_THREAD_TRACKER pThreadTracker;
  652. KIRQL oldIrql;
  653. ASSERT( pThreadPool != NULL );
  654. ASSERT( pThreadPool->Initialized );
  655. UlAcquireSpinLock( &pThreadPool->ThreadSpinLock, &oldIrql );
  656. if (IsListEmpty(&pThreadPool->ThreadListHead))
  657. {
  658. pThreadTracker = NULL;
  659. }
  660. else
  661. {
  662. pEntry = RemoveHeadList(&pThreadPool->ThreadListHead);
  663. pThreadTracker = CONTAINING_RECORD(
  664. pEntry,
  665. UL_THREAD_TRACKER,
  666. ThreadListEntry
  667. );
  668. }
  669. UlReleaseSpinLock( &pThreadPool->ThreadSpinLock, oldIrql );
  670. return pThreadTracker;
  671. } // UlpPopThreadTracker
  672. /***************************************************************************++
  673. Routine Description:
  674. A dummy function to indicate that the thread should be terminated.
  675. Arguments:
  676. pWorkItem - Supplies the dummy work item.
  677. Return Value:
  678. None
  679. --***************************************************************************/
  680. VOID
  681. UlpKillThreadWorker(
  682. IN PUL_WORK_ITEM pWorkItem
  683. )
  684. {
  685. return;
  686. } // UlpKillThreadWorker
  687. /***************************************************************************++
  688. Routine Description:
  689. A function that queues a worker item to a thread pool.
  690. Arguments:
  691. pWorkItem - Supplies the work item.
  692. pWorkRoutine - Supplies the work routine.
  693. Return Value:
  694. None
  695. --***************************************************************************/
  696. VOID
  697. UlQueueWorkItem(
  698. IN PUL_WORK_ITEM pWorkItem,
  699. IN PUL_WORK_ROUTINE pWorkRoutine
  700. REFERENCE_DEBUG_FORMAL_PARAMS
  701. )
  702. {
  703. PUL_THREAD_POOL pThreadPool;
  704. CLONG Cpu, NextCpu;
  705. //
  706. // Sanity check.
  707. //
  708. ASSERT( pWorkItem != NULL );
  709. ASSERT( pWorkRoutine != NULL );
  710. WRITE_REF_TRACE_LOG(
  711. g_pWorkItemTraceLog,
  712. REF_ACTION_QUEUE_WORK_ITEM,
  713. 0,
  714. pWorkItem,
  715. pFileName,
  716. LineNumber
  717. );
  718. //
  719. // Save the pointer to the worker routine, then queue the item.
  720. //
  721. pWorkItem->pWorkRoutine = pWorkRoutine;
  722. //
  723. // Queue the work item on the idle processor if possible.
  724. //
  725. NextCpu = KeGetCurrentProcessorNumber();
  726. for (Cpu = 0; Cpu < g_UlNumberOfProcessors; Cpu++, NextCpu++)
  727. {
  728. if (NextCpu >= g_UlNumberOfProcessors)
  729. {
  730. NextCpu = 0;
  731. }
  732. pThreadPool = &g_UlThreadPool[NextCpu].ThreadPool;
  733. if (ExQueryDepthSList(&pThreadPool->WorkQueueSList) <=
  734. g_UlMaxWorkQueueDepth)
  735. {
  736. QUEUE_UL_WORK_ITEM( pThreadPool, pWorkItem );
  737. return;
  738. }
  739. }
  740. //
  741. // Queue the work item on the current thread pool.
  742. //
  743. pThreadPool = CURRENT_THREAD_POOL();
  744. QUEUE_UL_WORK_ITEM( pThreadPool, pWorkItem );
  745. } // UlQueueWorkItem
  746. /***************************************************************************++
  747. Routine Description:
  748. A function that queues a blocking worker item to a special thread pool.
  749. Arguments:
  750. pWorkItem - Supplies the work item.
  751. pWorkRoutine - Supplies the work routine.
  752. Return Value:
  753. None
  754. --***************************************************************************/
  755. VOID
  756. UlQueueBlockingItem(
  757. IN PUL_WORK_ITEM pWorkItem,
  758. IN PUL_WORK_ROUTINE pWorkRoutine
  759. REFERENCE_DEBUG_FORMAL_PARAMS
  760. )
  761. {
  762. PUL_THREAD_POOL pThreadPool;
  763. //
  764. // Sanity check.
  765. //
  766. ASSERT( pWorkItem != NULL );
  767. ASSERT( pWorkRoutine != NULL );
  768. WRITE_REF_TRACE_LOG(
  769. g_pWorkItemTraceLog,
  770. REF_ACTION_QUEUE_BLOCKING_ITEM,
  771. 0,
  772. pWorkItem,
  773. pFileName,
  774. LineNumber
  775. );
  776. //
  777. // Save the pointer to the worker routine, then queue the item.
  778. //
  779. pWorkItem->pWorkRoutine = pWorkRoutine;
  780. //
  781. // Queue the work item on the special wait thread pool.
  782. //
  783. pThreadPool = WAIT_THREAD_POOL();
  784. QUEUE_UL_WORK_ITEM( pThreadPool, pWorkItem );
  785. } // UlQueueBlockingItem
  786. /***************************************************************************++
  787. Routine Description:
  788. A function that either queues a worker item to a thread pool if the
  789. caller is at DISPATCH_LEVEL/APC_LEVEL or it simply calls the work
  790. routine directly.
  791. Arguments:
  792. pWorkItem - Supplies the work item.
  793. pWorkRoutine - Supplies the work routine.
  794. Return Value:
  795. None
  796. --***************************************************************************/
  797. VOID
  798. UlCallPassive(
  799. IN PUL_WORK_ITEM pWorkItem,
  800. IN PUL_WORK_ROUTINE pWorkRoutine
  801. REFERENCE_DEBUG_FORMAL_PARAMS
  802. )
  803. {
  804. //
  805. // Sanity check.
  806. //
  807. ASSERT( pWorkItem != NULL );
  808. ASSERT( pWorkRoutine != NULL );
  809. WRITE_REF_TRACE_LOG(
  810. g_pWorkItemTraceLog,
  811. REF_ACTION_CALL_PASSIVE,
  812. 0,
  813. pWorkItem,
  814. pFileName,
  815. LineNumber
  816. );
  817. if (KeGetCurrentIrql() == PASSIVE_LEVEL)
  818. {
  819. //
  820. // Clear this for consistency with UlpThreadPoolWorker.
  821. //
  822. pWorkItem->pWorkRoutine = NULL;
  823. pWorkRoutine(pWorkItem);
  824. }
  825. else
  826. {
  827. UL_QUEUE_WORK_ITEM(pWorkItem, pWorkRoutine);
  828. }
  829. } // UlCallPassive
  830. /***************************************************************************++
  831. Routine Description:
  832. Queries the "IRP thread", the special worker thread used as the
  833. target for all asynchronous IRPs.
  834. Arguments:
  835. pWorkItem - Supplies the work item.
  836. pWorkRoutine - Supplies the work routine.
  837. Return Value:
  838. None
  839. --***************************************************************************/
  840. PETHREAD
  841. UlQueryIrpThread(
  842. VOID
  843. )
  844. {
  845. PUL_THREAD_POOL pThreadPool;
  846. //
  847. // Sanity check.
  848. //
  849. pThreadPool = CURRENT_THREAD_POOL();
  850. ASSERT( pThreadPool->Initialized );
  851. //
  852. // Return the IRP thread.
  853. //
  854. return pThreadPool->pIrpThread;
  855. } // UlQueryIrpThread