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.

1135 lines
26 KiB

  1. /*++
  2. Copyright (c) 1998-2001 Microsoft Corporation
  3. Module Name:
  4. pipeline.cxx
  5. Abstract:
  6. This module implements the pipeline package.
  7. Author:
  8. Keith Moore (keithmo) 10-Jun-1998
  9. Revision History:
  10. --*/
  11. #include <precomp.h>
  12. //
  13. // Private types.
  14. //
  15. //
  16. // The UL_PIPELINE_THREAD_CONTEXT structure is used as a parameter to the
  17. // pipeline worker threads. This structure enables primitive communication
  18. // between UlCreatePipeline() and the worker threads.
  19. //
  20. typedef struct _UL_PIPELINE_THREAD_CONTEXT
  21. {
  22. PUL_PIPELINE pPipeline;
  23. ULONG Processor;
  24. NTSTATUS Status;
  25. KEVENT InitCompleteEvent;
  26. PUL_PIPELINE_THREAD_DATA pThreadData;
  27. } UL_PIPELINE_THREAD_CONTEXT, *PUL_PIPELINE_THREAD_CONTEXT;
  28. //
  29. // Private prototypes.
  30. //
  31. BOOLEAN
  32. UlpWaitForWorkPipeline(
  33. IN PUL_PIPELINE pPipeline
  34. );
  35. PLIST_ENTRY
  36. UlpDequeueAllWorkPipeline(
  37. IN PUL_PIPELINE pPipeline,
  38. IN PUL_PIPELINE_QUEUE Queue
  39. );
  40. BOOLEAN
  41. UlpLockQueuePipeline(
  42. IN PUL_PIPELINE_QUEUE pQueue
  43. );
  44. VOID
  45. UlpUnlockQueuePipeline(
  46. IN PUL_PIPELINE_QUEUE pQueue
  47. );
  48. PUL_PIPELINE_QUEUE
  49. UlpFindNextQueuePipeline(
  50. IN PUL_PIPELINE pPipeline,
  51. IN OUT PUL_PIPELINE_QUEUE_ORDINAL pQueueOrdinal
  52. );
  53. VOID
  54. UlpPipelineWorkerThread(
  55. IN PVOID pContext
  56. );
  57. PUL_PIPELINE
  58. UlpPipelineWorkerThreadStartup(
  59. IN PUL_PIPELINE_THREAD_CONTEXT pContext
  60. );
  61. VOID
  62. UlpKillPipelineWorkerThreads(
  63. IN PUL_PIPELINE pPipeline
  64. );
  65. #ifdef ALLOC_PRAGMA
  66. #pragma alloc_text( INIT, UlCreatePipeline )
  67. #pragma alloc_text( INIT, UlInitializeQueuePipeline )
  68. #pragma alloc_text( PAGE, UlDestroyPipeline )
  69. #pragma alloc_text( PAGE, UlpLockQueuePipeline )
  70. #pragma alloc_text( PAGE, UlpUnlockQueuePipeline )
  71. #pragma alloc_text( PAGE, UlpFindNextQueuePipeline )
  72. #pragma alloc_text( PAGE, UlpPipelineWorkerThread )
  73. #pragma alloc_text( PAGE, UlpPipelineWorkerThreadStartup )
  74. #endif
  75. #if 0
  76. NOT PAGEABLE -- UlQueueWorkPipeline
  77. NOT PAGEABLE -- UlpWaitForWorkPipeline
  78. NOT PAGEABLE -- UlpDequeueAllWorkPipeline
  79. NOT PAGEABLE -- UlpKillPipelineWorkerThreads
  80. #endif
  81. //
  82. // Public functions.
  83. //
  84. /***************************************************************************++
  85. Routine Description:
  86. Creates a new pipeline and the associated queues.
  87. Arguments:
  88. ppPipeline - Receives a pointer to the new pipeline object.
  89. QueueCount - Supplies the number of queues in the new pipeline.
  90. ThreadsPerCpu - Supplies the number of worker threads to create
  91. per CPU.
  92. Return Value:
  93. NTSTATUS - Completion status.
  94. --***************************************************************************/
  95. NTSTATUS
  96. UlCreatePipeline(
  97. OUT PUL_PIPELINE * ppPipeline,
  98. IN SHORT QueueCount,
  99. IN SHORT ThreadsPerCpu
  100. )
  101. {
  102. NTSTATUS status;
  103. PVOID pAllocation;
  104. PUL_PIPELINE pPipeline;
  105. PUL_PIPELINE_QUEUE pQueue;
  106. PUL_PIPELINE_RARE pRareData;
  107. ULONG bytesRequired;
  108. ULONG totalThreads;
  109. ULONG i;
  110. PUL_PIPELINE_THREAD_DATA pThreadData;
  111. UL_PIPELINE_THREAD_CONTEXT context;
  112. OBJECT_ATTRIBUTES objectAttributes;
  113. //
  114. // Sanity check.
  115. //
  116. PAGED_CODE();
  117. ASSERT( QueueCount > 0 );
  118. ASSERT( ThreadsPerCpu > 0 );
  119. //
  120. // Allocate the pool. Note that we allocate slightly larger than
  121. // necessary then round up to the next cache-line. Also note that
  122. // we allocate the pipeline, rare data, and thread data structures
  123. // in a single pool block.
  124. //
  125. totalThreads = (ULONG)ThreadsPerCpu * g_UlNumberOfProcessors;
  126. bytesRequired = PIPELINE_LENGTH( QueueCount, totalThreads ) +
  127. CACHE_LINE_SIZE - 1;
  128. pAllocation = UL_ALLOCATE_POOL(
  129. NonPagedPool,
  130. bytesRequired,
  131. UL_PIPELINE_POOL_TAG
  132. );
  133. if( pAllocation == NULL )
  134. {
  135. return STATUS_INSUFFICIENT_RESOURCES;
  136. }
  137. RtlZeroMemory(
  138. pAllocation,
  139. bytesRequired
  140. );
  141. //
  142. // Ensure the pipeline starts on a cache boundary.
  143. //
  144. pPipeline = (PUL_PIPELINE)ROUND_UP( pAllocation, CACHE_LINE_SIZE );
  145. //
  146. // Initialize the static portion of the pipeline.
  147. //
  148. UlInitializeSpinLock( &pPipeline->PipelineLock, "PipelineLock" );
  149. pPipeline->ThreadsRunning = (SHORT)totalThreads;
  150. pPipeline->QueuesWithWork = 0;
  151. pPipeline->MaximumThreadsRunning = (SHORT)totalThreads;
  152. pPipeline->QueueCount = QueueCount;
  153. pPipeline->ShutdownFlag = FALSE;
  154. KeInitializeEvent(
  155. &pPipeline->WorkAvailableEvent, // Event
  156. SynchronizationEvent, // Type
  157. FALSE // State
  158. );
  159. //
  160. // Initialize the rare data. Note the PIPELINE_TO_RARE_DATA() macro
  161. // requires the QueueCount field to be set before the macro can be
  162. // used.
  163. //
  164. pRareData = PIPELINE_TO_RARE_DATA( pPipeline );
  165. pRareData->pAllocationBlock = pAllocation;
  166. //
  167. // Setup the thread start context. This structure allows us to
  168. // pass enough information to the worker thread that it can
  169. // set its affinity correctly and return any failure status
  170. // back to us.
  171. //
  172. context.pPipeline = pPipeline;
  173. context.Processor = 0;
  174. context.Status = STATUS_SUCCESS;
  175. KeInitializeEvent(
  176. &context.InitCompleteEvent, // Event
  177. SynchronizationEvent, // Type
  178. FALSE // State
  179. );
  180. //
  181. // Create the worker threads.
  182. //
  183. InitializeObjectAttributes(
  184. &objectAttributes, // ObjectAttributes
  185. NULL, // ObjectName
  186. UL_KERNEL_HANDLE, // Attributes
  187. NULL, // RootDirectory
  188. NULL // SecurityDescriptor
  189. );
  190. UlAttachToSystemProcess();
  191. pThreadData = PIPELINE_TO_THREAD_DATA( pPipeline );
  192. for (i = 0 ; i < totalThreads ; i++)
  193. {
  194. context.pThreadData = pThreadData;
  195. status = PsCreateSystemThread(
  196. &pThreadData->ThreadHandle, // ThreadHandle
  197. THREAD_ALL_ACCESS, // DesiredAccess
  198. &objectAttributes, // ObjectAttributes
  199. NULL, // ProcessHandle
  200. NULL, // ClientId
  201. &UlpPipelineWorkerThread, // StartRoutine
  202. &context // StartContext
  203. );
  204. if (!NT_SUCCESS(status))
  205. {
  206. UlDetachFromSystemProcess();
  207. UlDestroyPipeline( pPipeline );
  208. return status;
  209. }
  210. //
  211. // Wait for it to initialize.
  212. //
  213. KeWaitForSingleObject(
  214. &context.InitCompleteEvent, // Object
  215. UserRequest, // WaitReason
  216. KernelMode, // WaitMode
  217. FALSE, // Alertable
  218. NULL // Timeout
  219. );
  220. status = context.Status;
  221. if (!NT_SUCCESS(status))
  222. {
  223. //
  224. // The current thread failed initialization. Since we know it
  225. // has already exited, we can go ahead and close & NULL the
  226. // handle. This allows UlpKillPipelineWorkerThreads() to just
  227. // look at the first thread handle in the array to determine if
  228. // there are any threads that need to be terminated.
  229. //
  230. ZwClose( pThreadData->ThreadHandle );
  231. UlDetachFromSystemProcess();
  232. pThreadData->ThreadHandle = NULL;
  233. pThreadData->pThreadObject = NULL;
  234. UlDestroyPipeline( pPipeline );
  235. return status;
  236. }
  237. ASSERT( pThreadData->ThreadHandle != NULL );
  238. ASSERT( pThreadData->pThreadObject != NULL );
  239. //
  240. // Advance to the next thread.
  241. //
  242. pThreadData++;
  243. context.Processor++;
  244. if (context.Processor >= g_UlNumberOfProcessors)
  245. {
  246. context.Processor = 0;
  247. }
  248. }
  249. UlDetachFromSystemProcess();
  250. //
  251. // At this point, the pipeline is fully initialized *except* for
  252. // the individual work queues. It is the caller's responsibility
  253. // to initialize those queues.
  254. //
  255. *ppPipeline = pPipeline;
  256. return STATUS_SUCCESS;
  257. } // UlCreatePipeline
  258. /***************************************************************************++
  259. Routine Description:
  260. Initializes the specified pipeline queue.
  261. Arguments:
  262. pPipeline - Supplies the pipeline that owns the queue.
  263. QueueOrdinal - Supplies the queue to initialize.
  264. pHandler - Supplies the handler routine for the queue.
  265. --***************************************************************************/
  266. VOID
  267. UlInitializeQueuePipeline(
  268. IN PUL_PIPELINE pPipeline,
  269. IN UL_PIPELINE_QUEUE_ORDINAL QueueOrdinal,
  270. IN PFN_UL_PIPELINE_HANDLER pHandler
  271. )
  272. {
  273. PUL_PIPELINE_QUEUE pQueue;
  274. //
  275. // Sanity check.
  276. //
  277. PAGED_CODE();
  278. ASSERT( QueueOrdinal < pPipeline->QueueCount );
  279. //
  280. // Initialize it.
  281. //
  282. pQueue = &pPipeline->Queues[QueueOrdinal];
  283. InitializeListHead(
  284. &pQueue->WorkQueueHead
  285. );
  286. pQueue->QueueLock = L_UNLOCKED;
  287. pQueue->pHandler = pHandler;
  288. } // UlInitializeQueuePipeline
  289. /***************************************************************************++
  290. Routine Description:
  291. Destroys the specified pipeline, freeing any allocated resources
  292. and killing the worker threads.
  293. Arguments:
  294. pPipeline - Supplies the pipeline to destroy.
  295. Return Value:
  296. NTSTATUS - Completion status.
  297. --***************************************************************************/
  298. NTSTATUS
  299. UlDestroyPipeline(
  300. IN PUL_PIPELINE pPipeline
  301. )
  302. {
  303. //
  304. // Sanity check.
  305. //
  306. PAGED_CODE();
  307. ASSERT( pPipeline != NULL );
  308. ASSERT( PIPELINE_TO_RARE_DATA( pPipeline ) != NULL );
  309. ASSERT( PIPELINE_TO_RARE_DATA( pPipeline )->pAllocationBlock != NULL );
  310. UlpKillPipelineWorkerThreads( pPipeline );
  311. UL_FREE_POOL(
  312. PIPELINE_TO_RARE_DATA( pPipeline )->pAllocationBlock,
  313. UL_PIPELINE_POOL_TAG
  314. );
  315. return STATUS_SUCCESS;
  316. } // UlDestroyPipeline
  317. /***************************************************************************++
  318. Routine Description:
  319. Enqueues a work item to the specified pipeline queue.
  320. Arguments:
  321. pPipeline - Supplies the pipeline that owns the queue.
  322. QueueOrdinal - Supplies the queue to enqueue the work on.
  323. pWorkItem - Supplies the work item to enqueue.
  324. --***************************************************************************/
  325. VOID
  326. UlQueueWorkPipeline(
  327. IN PUL_PIPELINE pPipeline,
  328. IN UL_PIPELINE_QUEUE_ORDINAL QueueOrdinal,
  329. IN PUL_PIPELINE_WORK_ITEM pWorkItem
  330. )
  331. {
  332. KIRQL oldIrql;
  333. PUL_PIPELINE_QUEUE pQueue;
  334. BOOLEAN needToSetEvent = FALSE;
  335. ASSERT( QueueOrdinal < pPipeline->QueueCount );
  336. pQueue = &pPipeline->Queues[QueueOrdinal];
  337. UlAcquireSpinLock(
  338. &pPipeline->PipelineLock,
  339. &oldIrql
  340. );
  341. if (!pPipeline->ShutdownFlag)
  342. {
  343. //
  344. // If the queue is currently empty, then remember that we have new
  345. // queue with work pending. If the number of pending queues is now
  346. // greater than the number of threads currently running (but less
  347. // than the maximum configured), then remember that we'll need to
  348. // set the event to unblock a new worker thread.
  349. //
  350. if (IsListEmpty( &pQueue->WorkQueueHead ))
  351. {
  352. pPipeline->QueuesWithWork++;
  353. if (pPipeline->QueuesWithWork > pPipeline->ThreadsRunning &&
  354. pPipeline->ThreadsRunning < pPipeline->MaximumThreadsRunning)
  355. {
  356. needToSetEvent = TRUE;
  357. }
  358. }
  359. InsertTailList(
  360. &pQueue->WorkQueueHead,
  361. &pWorkItem->WorkQueueEntry
  362. );
  363. }
  364. UlReleaseSpinLock(
  365. &pPipeline->PipelineLock,
  366. oldIrql
  367. );
  368. if (needToSetEvent)
  369. {
  370. KeSetEvent(
  371. &pPipeline->WorkAvailableEvent, // Event
  372. g_UlPriorityBoost, // Increment
  373. FALSE // Wait
  374. );
  375. }
  376. } // UlQueueWorkPipeline
  377. //
  378. // Private functions.
  379. //
  380. /***************************************************************************++
  381. Routine Description:
  382. Blocks the current thread if necessary until work is available on one
  383. of the pipeline queues.
  384. Arguments:
  385. pPipeline - Supplies the pipeline to block on.
  386. Return Value:
  387. BOOLEAN - TRUE if the thread should exit, FALSE if it should continue
  388. to service requests.
  389. --***************************************************************************/
  390. BOOLEAN
  391. UlpWaitForWorkPipeline(
  392. IN PUL_PIPELINE pPipeline
  393. )
  394. {
  395. KIRQL oldIrql;
  396. BOOLEAN needToWait;
  397. BOOLEAN firstPass = TRUE;
  398. while (TRUE)
  399. {
  400. UlAcquireSpinLock(
  401. &pPipeline->PipelineLock,
  402. &oldIrql
  403. );
  404. if (pPipeline->ShutdownFlag)
  405. {
  406. UlReleaseSpinLock(
  407. &pPipeline->PipelineLock,
  408. oldIrql
  409. );
  410. return TRUE;
  411. }
  412. //
  413. // If there are more threads running than there is work available
  414. // or if we've reached our maximum thread count, then we'll need
  415. // to wait on the event. Otherwise, we can update the running thread
  416. // count and bail.
  417. //
  418. if (pPipeline->QueuesWithWork <= pPipeline->ThreadsRunning ||
  419. pPipeline->ThreadsRunning >= pPipeline->MaximumThreadsRunning)
  420. {
  421. needToWait = TRUE;
  422. if (firstPass)
  423. {
  424. pPipeline->ThreadsRunning--;
  425. firstPass = FALSE;
  426. }
  427. }
  428. else
  429. {
  430. pPipeline->ThreadsRunning++;
  431. needToWait = FALSE;
  432. }
  433. UlReleaseSpinLock(
  434. &pPipeline->PipelineLock,
  435. oldIrql
  436. );
  437. if (needToWait)
  438. {
  439. KeWaitForSingleObject(
  440. &pPipeline->WorkAvailableEvent, // Object
  441. UserRequest, // WaitReason
  442. KernelMode, // WaitMode
  443. FALSE, // Alertable
  444. NULL // Timeout
  445. );
  446. }
  447. else
  448. {
  449. break;
  450. }
  451. }
  452. return FALSE;
  453. } // UlpWaitForWorkPipeline
  454. /***************************************************************************++
  455. Routine Description:
  456. Dequeues all work items from the specified queue.
  457. Arguments:
  458. pPipeline - Supplies the pipeline owning the queue.
  459. pQueue - Supplies the queue dequeue the work items from.
  460. Return Value:
  461. PLIST_ENTRY - Pointer to the first work item if successful, or a
  462. pointer to the head of the work queue if it is empty.
  463. --***************************************************************************/
  464. PLIST_ENTRY
  465. UlpDequeueAllWorkPipeline(
  466. IN PUL_PIPELINE pPipeline,
  467. IN PUL_PIPELINE_QUEUE pQueue
  468. )
  469. {
  470. KIRQL oldIrql;
  471. PLIST_ENTRY pListEntry;
  472. UlAcquireSpinLock(
  473. &pPipeline->PipelineLock,
  474. &oldIrql
  475. );
  476. //
  477. // Dequeue all work items and update the count of queues with work.
  478. //
  479. pListEntry = pQueue->WorkQueueHead.Flink;
  480. if (pListEntry != &pQueue->WorkQueueHead)
  481. {
  482. pPipeline->QueuesWithWork--;
  483. }
  484. InitializeListHead( &pQueue->WorkQueueHead );
  485. UlReleaseSpinLock(
  486. &pPipeline->PipelineLock,
  487. oldIrql
  488. );
  489. return pListEntry;
  490. } // UlpDequeueAllWorkPipeline
  491. /***************************************************************************++
  492. Routine Description:
  493. Attempts to acquire the lock protecting the queue.
  494. N.B. Queue locks cannot be acquired recursively.
  495. Arguments:
  496. pQueue - Supplies the queue to attempt to lock.
  497. Return Value:
  498. BOOLEAN - TRUE if the queue was locked successfully, FALSE otherwise.
  499. --***************************************************************************/
  500. BOOLEAN
  501. UlpLockQueuePipeline(
  502. IN PUL_PIPELINE_QUEUE pQueue
  503. )
  504. {
  505. LONG result;
  506. //
  507. // Sanity check.
  508. //
  509. PAGED_CODE();
  510. //
  511. // Try to exchange the lock value with L_LOCKED. If the previous value
  512. // was L_UNLOCKED, then we know the lock is ours.
  513. //
  514. result = UlInterlockedCompareExchange(
  515. &pQueue->QueueLock,
  516. L_LOCKED,
  517. L_UNLOCKED
  518. );
  519. return ( result == L_UNLOCKED );
  520. } // UlpLockQueuePipeline
  521. /***************************************************************************++
  522. Routine Description:
  523. Unlocks a locked queue.
  524. Arguments:
  525. pQueue - Supplies the queue to unlock.
  526. --***************************************************************************/
  527. VOID
  528. UlpUnlockQueuePipeline(
  529. IN PUL_PIPELINE_QUEUE pQueue
  530. )
  531. {
  532. //
  533. // Sanity check.
  534. //
  535. PAGED_CODE();
  536. //
  537. // No need to interlock this, as we own the lock.
  538. //
  539. pQueue->QueueLock = L_UNLOCKED;
  540. } // UlpUnlockQueuePipeline
  541. /***************************************************************************++
  542. Routine Description:
  543. Scans the specified pipeline's queues looking for one that is not
  544. currently locked.
  545. N.B. The queues are searched backwards (from high index to low index).
  546. Arguments:
  547. pPipeline - Supplies the pipeline to scan.
  548. pQueueOrdinal - Supplies a pointer to the ordinal of the most recently
  549. touched queue. This value will get updated with the ordinal of the
  550. queue returned (if successful).
  551. Return Value:
  552. PUL_PIPELINE_QUEUE - Pointer to a queue if succesful, NULL otherwise.
  553. --***************************************************************************/
  554. PUL_PIPELINE_QUEUE
  555. UlpFindNextQueuePipeline(
  556. IN PUL_PIPELINE pPipeline,
  557. IN OUT PUL_PIPELINE_QUEUE_ORDINAL pQueueOrdinal
  558. )
  559. {
  560. UL_PIPELINE_QUEUE_ORDINAL ordinal;
  561. SHORT count;
  562. PUL_PIPELINE_QUEUE pQueue;
  563. //
  564. // Sanity check.
  565. //
  566. PAGED_CODE();
  567. //
  568. // Start at the current values.
  569. //
  570. ordinal = *pQueueOrdinal;
  571. pQueue = &pPipeline->Queues[ordinal];
  572. for (count = pPipeline->QueueCount ; count > 0 ; count--)
  573. {
  574. //
  575. // Go to the previous value. We scan backwards to make the
  576. // math (and the generated code) a bit simpler.
  577. //
  578. pQueue--;
  579. ordinal--;
  580. if (ordinal < 0)
  581. {
  582. ordinal = pPipeline->QueueCount - 1;
  583. pQueue = &pPipeline->Queues[ordinal];
  584. }
  585. //
  586. // If the queue is not empty and we can lock the queue,
  587. // then return it.
  588. //
  589. if (!IsListEmpty( &pQueue->WorkQueueHead ) &&
  590. UlpLockQueuePipeline( pQueue ))
  591. {
  592. *pQueueOrdinal = ordinal;
  593. return pQueue;
  594. }
  595. }
  596. //
  597. // If we made it this far, then all queues are being handled.
  598. //
  599. return NULL;
  600. } // UlpFindNextQueuePipeline
  601. /***************************************************************************++
  602. Routine Description:
  603. Worker thread for the pipeline package.
  604. Arguments:
  605. pContext - Supplies the context for the thread. This is actually a
  606. PUL_PIPELINE_THREAD_CONTEXT pointer containing startup information
  607. for this thread.
  608. --***************************************************************************/
  609. VOID
  610. UlpPipelineWorkerThread(
  611. IN PVOID pContext
  612. )
  613. {
  614. PUL_PIPELINE pPipeline;
  615. PUL_PIPELINE_QUEUE pQueue;
  616. PUL_PIPELINE_WORK_ITEM pWorkItem;
  617. UL_PIPELINE_QUEUE_ORDINAL ordinal;
  618. PFN_UL_PIPELINE_HANDLER pHandler;
  619. PUL_PIPELINE_THREAD_DATA pThreadData;
  620. PLIST_ENTRY pListEntry;
  621. //
  622. // Sanity check.
  623. //
  624. PAGED_CODE();
  625. ASSERT( pContext != NULL );
  626. //
  627. // Initialize the thread data.
  628. //
  629. pThreadData = ((PUL_PIPELINE_THREAD_CONTEXT)pContext)->pThreadData;
  630. pThreadData->pThreadObject = (PVOID)PsGetCurrentThread();
  631. //
  632. // Initialize the thread. If this fails, we're toast.
  633. //
  634. pPipeline = UlpPipelineWorkerThreadStartup(
  635. (PUL_PIPELINE_THREAD_CONTEXT)pContext
  636. );
  637. if (!pPipeline)
  638. {
  639. return;
  640. }
  641. //
  642. // Loop forever, or at least until we're told to quit.
  643. //
  644. ordinal = 0;
  645. while (TRUE)
  646. {
  647. //
  648. // Wait for something to do.
  649. //
  650. if (UlpWaitForWorkPipeline( pPipeline ))
  651. {
  652. break;
  653. }
  654. //
  655. // Try to find an unowned queue.
  656. //
  657. pQueue = UlpFindNextQueuePipeline( pPipeline, &ordinal );
  658. if (pQueue != NULL)
  659. {
  660. #if DBG
  661. pThreadData->QueuesHandled++;
  662. #endif
  663. //
  664. // Snag the handler routine from the queue.
  665. //
  666. pHandler = pQueue->pHandler;
  667. //
  668. // Loop through all of the work items on the queue and
  669. // invoke the handler for each.
  670. //
  671. pListEntry = UlpDequeueAllWorkPipeline(
  672. pPipeline,
  673. pQueue
  674. );
  675. while (pListEntry != &pQueue->WorkQueueHead)
  676. {
  677. pWorkItem = CONTAINING_RECORD(
  678. pListEntry,
  679. UL_PIPELINE_WORK_ITEM,
  680. WorkQueueEntry
  681. );
  682. pListEntry = pListEntry->Flink;
  683. (pHandler)( pWorkItem );
  684. #if DBG
  685. pThreadData->WorkItemsHandled++;
  686. #endif
  687. }
  688. //
  689. // We're done with the queue, so unlock it.
  690. //
  691. UlpUnlockQueuePipeline( pQueue );
  692. }
  693. }
  694. } // UlpPipelineWorkerThread
  695. /***************************************************************************++
  696. Routine Description:
  697. Startup code for pipeline worker threads.
  698. Arguments:
  699. pContext - Supplies a pointer to the startup context for this thread.
  700. The context will receive the final startup completion status.
  701. Return Value:
  702. PUL_PIPELINE - A pointer to the thread's pipeline if successful,
  703. NULL otherwise.
  704. --***************************************************************************/
  705. PUL_PIPELINE
  706. UlpPipelineWorkerThreadStartup(
  707. IN PUL_PIPELINE_THREAD_CONTEXT pContext
  708. )
  709. {
  710. NTSTATUS status;
  711. ULONG affinityMask;
  712. PUL_PIPELINE pPipeline;
  713. //
  714. // Sanity check.
  715. //
  716. PAGED_CODE();
  717. ASSERT( pContext != NULL );
  718. ASSERT( pContext->pPipeline != NULL );
  719. ASSERT( pContext->pThreadData != NULL );
  720. //
  721. // Disable hard error popups.
  722. //
  723. IoSetThreadHardErrorMode( FALSE );
  724. //
  725. // Set our affinity.
  726. //
  727. ASSERT( pContext->Processor < g_UlNumberOfProcessors );
  728. affinityMask = 1 << pContext->Processor;
  729. status = NtSetInformationThread(
  730. NtCurrentThread(), // ThreadHandle
  731. ThreadAffinityMask, // ThreadInformationClass
  732. &affinityMask, // ThreadInformation
  733. sizeof(affinityMask) // ThreadInformationLength
  734. );
  735. if (!NT_SUCCESS(status))
  736. {
  737. pContext->Status = status;
  738. return NULL;
  739. }
  740. //
  741. // Capture the pipeline from the context before setting the
  742. // init complete event. Once we set the event, we are not allowed
  743. // to touch the context structure.
  744. //
  745. pPipeline = pContext->pPipeline;
  746. ASSERT( pPipeline != NULL );
  747. KeSetEvent(
  748. &pContext->InitCompleteEvent, // Event
  749. 0, // Increment
  750. FALSE // Wait
  751. );
  752. return pPipeline;
  753. } // UlpPipelineWorkerThreadStartup
  754. /***************************************************************************++
  755. Routine Description:
  756. Kills all worker threads associated with the given pipeline and
  757. waits for them to die.
  758. Arguments:
  759. pPipeline - Supplies the pipeline to terminate.
  760. --***************************************************************************/
  761. VOID
  762. UlpKillPipelineWorkerThreads(
  763. IN PUL_PIPELINE pPipeline
  764. )
  765. {
  766. KIRQL oldIrql;
  767. SHORT totalThreads;
  768. SHORT i;
  769. PUL_PIPELINE_THREAD_DATA pThreadData;
  770. LARGE_INTEGER delayInterval;
  771. //
  772. // Sanity check.
  773. //
  774. ASSERT( pPipeline != NULL );
  775. //
  776. // Count the number of running threads.
  777. //
  778. pThreadData = PIPELINE_TO_THREAD_DATA( pPipeline );
  779. totalThreads = 0;
  780. for (i = 0 ; i < pPipeline->MaximumThreadsRunning ; i++)
  781. {
  782. if (pThreadData->ThreadHandle == NULL)
  783. {
  784. break;
  785. }
  786. pThreadData++;
  787. totalThreads++;
  788. }
  789. if (totalThreads > 0)
  790. {
  791. //
  792. // We have threads running, so we'll need to signal them to stop.
  793. //
  794. UlAcquireSpinLock(
  795. &pPipeline->PipelineLock,
  796. &oldIrql
  797. );
  798. pPipeline->ShutdownFlag = TRUE;
  799. UlReleaseSpinLock(
  800. &pPipeline->PipelineLock,
  801. oldIrql
  802. );
  803. while (pPipeline->ThreadsRunning > 0)
  804. {
  805. KeSetEvent(
  806. &pPipeline->WorkAvailableEvent, // Event
  807. g_UlPriorityBoost, // Increment
  808. FALSE // Wait
  809. );
  810. delayInterval.QuadPart = -1*10*1000*500; // .5 second
  811. KeDelayExecutionThread(
  812. KernelMode, // WaitMode
  813. FALSE, // Alertable
  814. &delayInterval // Interval
  815. );
  816. }
  817. //
  818. // Wait for them to die.
  819. //
  820. pThreadData = PIPELINE_TO_THREAD_DATA( pPipeline );
  821. for (i = 0 ; i < totalThreads ; i++)
  822. {
  823. KeWaitForSingleObject(
  824. (PVOID)pThreadData->pThreadObject, // Object
  825. UserRequest, // WaitReason
  826. KernelMode, // WaitMode
  827. FALSE, // Alertable
  828. NULL // Timeout
  829. );
  830. UlCloseSystemHandle( pThreadData->ThreadHandle );
  831. pThreadData->ThreadHandle = NULL;
  832. pThreadData->pThreadObject = NULL;
  833. pThreadData++;
  834. }
  835. }
  836. } // UlpKillPipelineWorkerThreads