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.

1671 lines
43 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. worker.c
  5. Abstract:
  6. This module implements a worker thread and a set of functions for
  7. passing work to it.
  8. Author:
  9. Steve Wood (stevewo) 25-Jul-1991
  10. Revision History:
  11. --*/
  12. #include "exp.h"
  13. //
  14. // Define balance set wait object types.
  15. //
  16. typedef enum _BALANCE_OBJECT {
  17. TimerExpiration,
  18. ThreadSetManagerEvent,
  19. ShutdownEvent,
  20. MaximumBalanceObject
  21. } BALANCE_OBJECT;
  22. //
  23. // If this assertion fails then we must supply our own array of wait blocks.
  24. //
  25. C_ASSERT(MaximumBalanceObject <= THREAD_WAIT_OBJECTS);
  26. //
  27. // This is the structure passed around during shutdown
  28. //
  29. typedef struct {
  30. WORK_QUEUE_ITEM WorkItem;
  31. WORK_QUEUE_TYPE QueueType;
  32. PETHREAD PrevThread;
  33. } SHUTDOWN_WORK_ITEM, *PSHUTDOWN_WORK_ITEM;
  34. //
  35. // Used for disabling stack swapping
  36. //
  37. typedef struct _EXP_WORKER_LINK {
  38. LIST_ENTRY List;
  39. PETHREAD Thread;
  40. struct _EXP_WORKER_LINK **StackRef;
  41. } EXP_WORKER_LINK, *PEXP_WORKER_LINK;
  42. //
  43. // Define priorities for delayed and critical worker threads.
  44. // Note that these do not run at realtime.
  45. //
  46. // They run at csrss and below csrss to avoid pre-empting the
  47. // user interface under heavy load.
  48. //
  49. #define DELAYED_WORK_QUEUE_PRIORITY (12 - NORMAL_BASE_PRIORITY)
  50. #define CRITICAL_WORK_QUEUE_PRIORITY (13 - NORMAL_BASE_PRIORITY)
  51. #define HYPER_CRITICAL_WORK_QUEUE_PRIORITY (15 - NORMAL_BASE_PRIORITY)
  52. //
  53. // Number of worker threads to create for each type of system.
  54. //
  55. #define MAX_ADDITIONAL_THREADS 16
  56. #define MAX_ADDITIONAL_DYNAMIC_THREADS 16
  57. #define SMALL_NUMBER_OF_THREADS 2
  58. #define MEDIUM_NUMBER_OF_THREADS 3
  59. #define LARGE_NUMBER_OF_THREADS 5
  60. //
  61. // 10-minute timeout used for terminating dynamic work item worker threads.
  62. //
  63. #define DYNAMIC_THREAD_TIMEOUT ((LONGLONG)10 * 60 * 1000 * 1000 * 10)
  64. //
  65. // 1-second timeout used for waking up the worker thread set manager.
  66. //
  67. #define THREAD_SET_INTERVAL (1 * 1000 * 1000 * 10)
  68. //
  69. // Flag to pass in to the worker thread, indicating whether it is dynamic
  70. // or not.
  71. //
  72. #define DYNAMIC_WORKER_THREAD 0x80000000
  73. //
  74. // Per-queue dynamic thread state.
  75. //
  76. EX_WORK_QUEUE ExWorkerQueue[MaximumWorkQueue];
  77. //
  78. // Additional worker threads... Controlled using registry settings
  79. //
  80. ULONG ExpAdditionalCriticalWorkerThreads;
  81. ULONG ExpAdditionalDelayedWorkerThreads;
  82. ULONG ExCriticalWorkerThreads;
  83. ULONG ExDelayedWorkerThreads;
  84. //
  85. // Global events to wake up the thread set manager.
  86. //
  87. KEVENT ExpThreadSetManagerEvent;
  88. KEVENT ExpThreadSetManagerShutdownEvent;
  89. //
  90. // A reference to the balance manager thread, so that shutdown can
  91. // wait for it to terminate.
  92. //
  93. PETHREAD ExpWorkerThreadBalanceManagerPtr;
  94. //
  95. // A pointer to the last worker thread to exit (so the balance manager
  96. // can wait for it before exiting).
  97. //
  98. PETHREAD ExpLastWorkerThread;
  99. //
  100. // These are used to keep track of the set of workers, and whether or
  101. // not we're allowing them to be paged. Note that we can't use this
  102. // list for shutdown (sadly), as we can't just terminate the threads,
  103. // we need to flush their queues.
  104. //
  105. FAST_MUTEX ExpWorkerSwapinMutex;
  106. LIST_ENTRY ExpWorkerListHead;
  107. BOOLEAN ExpWorkersCanSwap;
  108. //
  109. // Worker queue item that can be filled in by the kernel debugger
  110. // to get code to run on the system.
  111. //
  112. WORK_QUEUE_ITEM ExpDebuggerWorkItem;
  113. PVOID ExpDebuggerProcessKill;
  114. PVOID ExpDebuggerProcessAttach;
  115. PVOID ExpDebuggerPageIn;
  116. ULONG ExpDebuggerWork;
  117. VOID
  118. ExpCheckDynamicThreadCount (
  119. VOID
  120. );
  121. NTSTATUS
  122. ExpCreateWorkerThread (
  123. WORK_QUEUE_TYPE QueueType,
  124. BOOLEAN Dynamic
  125. );
  126. VOID
  127. ExpDetectWorkerThreadDeadlock (
  128. VOID
  129. );
  130. VOID
  131. ExpWorkerThreadBalanceManager (
  132. IN PVOID StartContext
  133. );
  134. VOID
  135. ExpSetSwappingKernelApc (
  136. IN PKAPC Apc,
  137. OUT PKNORMAL_ROUTINE *NormalRoutine,
  138. IN OUT PVOID NormalContext,
  139. IN OUT PVOID *SystemArgument1,
  140. IN OUT PVOID *SystemArgument2
  141. );
  142. //
  143. // Procedure prototypes for the worker threads.
  144. //
  145. VOID
  146. ExpWorkerThread (
  147. IN PVOID StartContext
  148. );
  149. LOGICAL
  150. ExpCheckQueueShutdown (
  151. IN WORK_QUEUE_TYPE QueueType,
  152. IN PSHUTDOWN_WORK_ITEM ShutdownItem
  153. );
  154. VOID
  155. ExpShutdownWorker (
  156. IN PVOID Parameter
  157. );
  158. VOID
  159. ExpDebuggerWorker(
  160. IN PVOID Context
  161. );
  162. #ifdef ALLOC_PRAGMA
  163. #pragma alloc_text(INIT, ExpWorkerInitialization)
  164. #pragma alloc_text(PAGE, ExpCheckDynamicThreadCount)
  165. #pragma alloc_text(PAGE, ExpCreateWorkerThread)
  166. #pragma alloc_text(PAGE, ExpDetectWorkerThreadDeadlock)
  167. #pragma alloc_text(PAGE, ExpWorkerThreadBalanceManager)
  168. #pragma alloc_text(PAGE, ExSwapinWorkerThreads)
  169. #pragma alloc_text(PAGEKD, ExpDebuggerWorker)
  170. #pragma alloc_text(PAGELK, ExpSetSwappingKernelApc)
  171. #pragma alloc_text(PAGELK, ExpCheckQueueShutdown)
  172. #pragma alloc_text(PAGELK, ExpShutdownWorker)
  173. #pragma alloc_text(PAGELK, ExpShutdownWorkerThreads)
  174. #endif
  175. LOGICAL
  176. __forceinline
  177. ExpNewThreadNecessary (
  178. IN PEX_WORK_QUEUE Queue
  179. )
  180. /*++
  181. Routine Description:
  182. This function checks the supplied worker queue and determines whether
  183. it is appropriate to spin up a dynamic worker thread for that queue.
  184. Arguments:
  185. Queue - Supplies the queue that should be examined.
  186. Return Value:
  187. TRUE if the given work queue would benefit from the creation of an
  188. additional thread, FALSE if not.
  189. --*/
  190. {
  191. if ((Queue->Info.MakeThreadsAsNecessary == 1) &&
  192. (IsListEmpty (&Queue->WorkerQueue.EntryListHead) == FALSE) &&
  193. (Queue->WorkerQueue.CurrentCount < Queue->WorkerQueue.MaximumCount) &&
  194. (Queue->DynamicThreadCount < MAX_ADDITIONAL_DYNAMIC_THREADS)) {
  195. //
  196. // We know these things:
  197. //
  198. // - This queue is eligible for dynamic creation of threads to try
  199. // to keep the CPUs busy,
  200. //
  201. // - There are work items waiting in the queue,
  202. //
  203. // - The number of runable worker threads for this queue is less than
  204. // the number of processors on this system, and
  205. //
  206. // - We haven't reached the maximum dynamic thread count.
  207. //
  208. // An additional worker thread at this point will help clear the
  209. // backlog.
  210. //
  211. return TRUE;
  212. }
  213. //
  214. // One of the above conditions is false.
  215. //
  216. return FALSE;
  217. }
  218. NTSTATUS
  219. ExpWorkerInitialization (
  220. VOID
  221. )
  222. {
  223. ULONG Index;
  224. OBJECT_ATTRIBUTES ObjectAttributes;
  225. ULONG NumberOfDelayedThreads;
  226. ULONG NumberOfCriticalThreads;
  227. ULONG NumberOfThreads;
  228. NTSTATUS Status;
  229. HANDLE Thread;
  230. BOOLEAN NtAs;
  231. WORK_QUEUE_TYPE WorkQueueType;
  232. ExInitializeFastMutex (&ExpWorkerSwapinMutex);
  233. InitializeListHead (&ExpWorkerListHead);
  234. ExpWorkersCanSwap = TRUE;
  235. //
  236. // Set the number of worker threads based on the system size.
  237. //
  238. NtAs = MmIsThisAnNtAsSystem();
  239. NumberOfCriticalThreads = MEDIUM_NUMBER_OF_THREADS;
  240. //
  241. // 2001-07-13 CenkE Incremented boot time number of delayed threads.
  242. // We did this in Windows XP, because 3COM NICs would take a long
  243. // time with the network stack tying up the delayed worker threads.
  244. // When Mm would need a worker thread to load a driver on the critical
  245. // path of boot, it would also get stuck for a few seconds and hurt
  246. // boot times. Ideally we'd spawn new delayed threads as necessary as
  247. // well to prevent such contention from hurting boot and resume.
  248. //
  249. NumberOfDelayedThreads = MEDIUM_NUMBER_OF_THREADS + 4;
  250. switch (MmQuerySystemSize()) {
  251. case MmSmallSystem:
  252. break;
  253. case MmMediumSystem:
  254. if (NtAs) {
  255. NumberOfCriticalThreads += MEDIUM_NUMBER_OF_THREADS;
  256. }
  257. break;
  258. case MmLargeSystem:
  259. NumberOfCriticalThreads = LARGE_NUMBER_OF_THREADS;
  260. if (NtAs) {
  261. NumberOfCriticalThreads += LARGE_NUMBER_OF_THREADS;
  262. }
  263. break;
  264. default:
  265. break;
  266. }
  267. //
  268. // Initialize the work Queue objects.
  269. //
  270. if (ExpAdditionalCriticalWorkerThreads > MAX_ADDITIONAL_THREADS) {
  271. ExpAdditionalCriticalWorkerThreads = MAX_ADDITIONAL_THREADS;
  272. }
  273. if (ExpAdditionalDelayedWorkerThreads > MAX_ADDITIONAL_THREADS) {
  274. ExpAdditionalDelayedWorkerThreads = MAX_ADDITIONAL_THREADS;
  275. }
  276. //
  277. // Initialize the ExWorkerQueue[] array.
  278. //
  279. RtlZeroMemory (&ExWorkerQueue[0], MaximumWorkQueue * sizeof(EX_WORK_QUEUE));
  280. for (WorkQueueType = 0; WorkQueueType < MaximumWorkQueue; WorkQueueType += 1) {
  281. KeInitializeQueue (&ExWorkerQueue[WorkQueueType].WorkerQueue, 0);
  282. ExWorkerQueue[WorkQueueType].Info.WaitMode = UserMode;
  283. }
  284. //
  285. // Always make stack for this thread resident
  286. // so that worker pool deadlock magic can run
  287. // even when what we are trying to do is inpage
  288. // the hyper critical worker thread's stack.
  289. // Without this fix, we hold the process lock
  290. // but this thread's stack can't come in, and
  291. // the deadlock detection cannot create new threads
  292. // to break the system deadlock.
  293. //
  294. ExWorkerQueue[HyperCriticalWorkQueue].Info.WaitMode = KernelMode;
  295. if (NtAs) {
  296. ExWorkerQueue[CriticalWorkQueue].Info.WaitMode = KernelMode;
  297. }
  298. //
  299. // We only create dynamic threads for the critical work queue (note
  300. // this doesn't apply to dynamic threads created to break deadlocks.)
  301. //
  302. // The rationale is this: folks who use the delayed work queue are
  303. // not time critical, and the hypercritical queue is used rarely
  304. // by folks who are non-blocking.
  305. //
  306. ExWorkerQueue[CriticalWorkQueue].Info.MakeThreadsAsNecessary = 1;
  307. //
  308. // Initialize the global thread set manager events
  309. //
  310. KeInitializeEvent (&ExpThreadSetManagerEvent,
  311. SynchronizationEvent,
  312. FALSE);
  313. KeInitializeEvent (&ExpThreadSetManagerShutdownEvent,
  314. SynchronizationEvent,
  315. FALSE);
  316. //
  317. // Create the desired number of executive worker threads for each
  318. // of the work queues.
  319. //
  320. //
  321. // Create the builtin critical worker threads.
  322. //
  323. NumberOfThreads = NumberOfCriticalThreads + ExpAdditionalCriticalWorkerThreads;
  324. for (Index = 0; Index < NumberOfThreads; Index += 1) {
  325. //
  326. // Create a worker thread to service the critical work queue.
  327. //
  328. Status = ExpCreateWorkerThread (CriticalWorkQueue, FALSE);
  329. if (!NT_SUCCESS(Status)) {
  330. break;
  331. }
  332. }
  333. ExCriticalWorkerThreads += Index;
  334. //
  335. // Create the delayed worker threads.
  336. //
  337. NumberOfThreads = NumberOfDelayedThreads + ExpAdditionalDelayedWorkerThreads;
  338. for (Index = 0; Index < NumberOfThreads; Index += 1) {
  339. //
  340. // Create a worker thread to service the delayed work queue.
  341. //
  342. Status = ExpCreateWorkerThread (DelayedWorkQueue, FALSE);
  343. if (!NT_SUCCESS(Status)) {
  344. break;
  345. }
  346. }
  347. ExDelayedWorkerThreads += Index;
  348. //
  349. // Create the hypercritical worker thread.
  350. //
  351. Status = ExpCreateWorkerThread (HyperCriticalWorkQueue, FALSE);
  352. //
  353. // Create the worker thread set manager thread.
  354. //
  355. InitializeObjectAttributes (&ObjectAttributes, NULL, 0, NULL, NULL);
  356. Status = PsCreateSystemThread (&Thread,
  357. THREAD_ALL_ACCESS,
  358. &ObjectAttributes,
  359. 0,
  360. NULL,
  361. ExpWorkerThreadBalanceManager,
  362. NULL);
  363. if (NT_SUCCESS(Status)) {
  364. Status = ObReferenceObjectByHandle (Thread,
  365. SYNCHRONIZE,
  366. NULL,
  367. KernelMode,
  368. &ExpWorkerThreadBalanceManagerPtr,
  369. NULL);
  370. ZwClose (Thread);
  371. }
  372. return Status;
  373. }
  374. VOID
  375. ExQueueWorkItem (
  376. IN PWORK_QUEUE_ITEM WorkItem,
  377. IN WORK_QUEUE_TYPE QueueType
  378. )
  379. /*++
  380. Routine Description:
  381. This function inserts a work item into a work queue that is processed
  382. by a worker thread of the corresponding type.
  383. Arguments:
  384. WorkItem - Supplies a pointer to the work item to add the the queue.
  385. This structure must be located in NonPagedPool. The work item
  386. structure contains a doubly linked list entry, the address of a
  387. routine to call and a parameter to pass to that routine.
  388. QueueType - Specifies the type of work queue that the work item
  389. should be placed in.
  390. Return Value:
  391. None.
  392. --*/
  393. {
  394. PEX_WORK_QUEUE Queue;
  395. ASSERT (QueueType < MaximumWorkQueue);
  396. ASSERT (WorkItem->List.Flink == NULL);
  397. Queue = &ExWorkerQueue[QueueType];
  398. //
  399. // Insert the work item in the appropriate queue object.
  400. //
  401. KeInsertQueue (&Queue->WorkerQueue, &WorkItem->List);
  402. //
  403. // We check the queue's shutdown state after we insert the work
  404. // item to avoid the race condition when the queue's marked
  405. // between checking the queue and inserting the item. It's
  406. // possible for the queue to be marked for shutdown between the
  407. // insert and this assert (so the insert would've barely sneaked
  408. // in), but it's not worth guarding against this -- barely
  409. // sneaking in is not a good design strategy, and at this point in
  410. // the shutdown sequence, the caller simply should not be trying
  411. // to insert new queue items.
  412. //
  413. ASSERT (!Queue->Info.QueueDisabled);
  414. //
  415. // Determine whether another thread should be created, and signal the
  416. // thread set balance manager if so.
  417. //
  418. if (ExpNewThreadNecessary (Queue) != FALSE) {
  419. KeSetEvent (&ExpThreadSetManagerEvent, 0, FALSE);
  420. }
  421. return;
  422. }
  423. VOID
  424. ExpWorkerThreadBalanceManager (
  425. IN PVOID StartContext
  426. )
  427. /*++
  428. Routine Description:
  429. This function is the startup code for the worker thread manager thread.
  430. The worker thread manager thread is created during system initialization
  431. and begins execution in this function.
  432. This thread is responsible for detecting and breaking circular deadlocks
  433. in the system worker thread queues. It will also create and destroy
  434. additional worker threads as needed based on loading.
  435. Arguments:
  436. Context - Supplies a pointer to an arbitrary data structure (NULL).
  437. Return Value:
  438. None.
  439. --*/
  440. {
  441. KTIMER PeriodTimer;
  442. LARGE_INTEGER DueTime;
  443. PVOID WaitObjects[MaximumBalanceObject];
  444. NTSTATUS Status;
  445. PAGED_CODE();
  446. UNREFERENCED_PARAMETER (StartContext);
  447. //
  448. // Raise the thread priority to just higher than the priority of the
  449. // critical work queue.
  450. //
  451. KeSetBasePriorityThread (KeGetCurrentThread(),
  452. CRITICAL_WORK_QUEUE_PRIORITY + 1);
  453. //
  454. // Initialize the periodic timer and set the manager period.
  455. //
  456. KeInitializeTimer (&PeriodTimer);
  457. DueTime.QuadPart = - THREAD_SET_INTERVAL;
  458. //
  459. // Initialize the wait object array.
  460. //
  461. WaitObjects[TimerExpiration] = (PVOID)&PeriodTimer;
  462. WaitObjects[ThreadSetManagerEvent] = (PVOID)&ExpThreadSetManagerEvent;
  463. WaitObjects[ShutdownEvent] = (PVOID)&ExpThreadSetManagerShutdownEvent;
  464. //
  465. // Loop forever processing events.
  466. //
  467. while (TRUE) {
  468. //
  469. // Set the timer to expire at the next periodic interval.
  470. //
  471. KeSetTimer (&PeriodTimer, DueTime, NULL);
  472. //
  473. // Wake up when the timer expires or the set manager event is
  474. // signalled.
  475. //
  476. Status = KeWaitForMultipleObjects (MaximumBalanceObject,
  477. WaitObjects,
  478. WaitAny,
  479. Executive,
  480. KernelMode,
  481. FALSE,
  482. NULL,
  483. NULL);
  484. switch (Status) {
  485. case TimerExpiration:
  486. //
  487. // Periodic timer expiration - go see if any work queues
  488. // are deadlocked.
  489. //
  490. ExpDetectWorkerThreadDeadlock ();
  491. break;
  492. case ThreadSetManagerEvent:
  493. //
  494. // Someone has asked us to check some metrics to determine
  495. // whether we should create another worker thread.
  496. //
  497. ExpCheckDynamicThreadCount ();
  498. break;
  499. case ShutdownEvent:
  500. //
  501. // Time to exit...
  502. //
  503. KeCancelTimer (&PeriodTimer);
  504. ASSERT (ExpLastWorkerThread);
  505. //
  506. // Wait for the last worker thread to terminate
  507. //
  508. KeWaitForSingleObject (ExpLastWorkerThread,
  509. Executive,
  510. KernelMode,
  511. FALSE,
  512. NULL);
  513. ObDereferenceObject (ExpLastWorkerThread);
  514. PsTerminateSystemThread(STATUS_SYSTEM_SHUTDOWN);
  515. break;
  516. }
  517. //
  518. // Special debugger support.
  519. //
  520. // This checks if special debugging routines need to be run on the
  521. // behalf of the debugger.
  522. //
  523. if (ExpDebuggerWork == 1) {
  524. ExInitializeWorkItem(&ExpDebuggerWorkItem, ExpDebuggerWorker, NULL);
  525. ExpDebuggerWork = 2;
  526. ExQueueWorkItem(&ExpDebuggerWorkItem, DelayedWorkQueue);
  527. }
  528. }
  529. }
  530. VOID
  531. ExpCheckDynamicThreadCount (
  532. VOID
  533. )
  534. /*++
  535. Routine Description:
  536. This routine is called when there is reason to believe that a work queue
  537. might benefit from the creation of an additional worker thread.
  538. This routine checks each queue to determine whether it would benefit from
  539. an additional worker thread (see ExpNewThreadNecessary()), and creates
  540. one if so.
  541. Arguments:
  542. None.
  543. Return Value:
  544. None.
  545. --*/
  546. {
  547. PEX_WORK_QUEUE Queue;
  548. WORK_QUEUE_TYPE QueueType;
  549. PAGED_CODE();
  550. //
  551. // Check each worker queue.
  552. //
  553. Queue = &ExWorkerQueue[0];
  554. for (QueueType = 0; QueueType < MaximumWorkQueue; Queue += 1, QueueType += 1) {
  555. if (ExpNewThreadNecessary (Queue)) {
  556. //
  557. // Create a new thread for this queue. We explicitly ignore
  558. // an error from ExpCreateDynamicThread(): there's nothing
  559. // we can or should do in the event of a failure.
  560. //
  561. ExpCreateWorkerThread (QueueType, TRUE);
  562. }
  563. }
  564. }
  565. VOID
  566. ExpDetectWorkerThreadDeadlock (
  567. VOID
  568. )
  569. /*++
  570. Routine Description:
  571. This function creates new work item threads if a possible deadlock is
  572. detected.
  573. Arguments:
  574. None.
  575. Return Value:
  576. None
  577. --*/
  578. {
  579. ULONG Index;
  580. PEX_WORK_QUEUE Queue;
  581. PAGED_CODE();
  582. //
  583. // Process each queue type.
  584. //
  585. for (Index = 0; Index < MaximumWorkQueue; Index += 1) {
  586. Queue = &ExWorkerQueue[Index];
  587. ASSERT( Queue->DynamicThreadCount <= MAX_ADDITIONAL_DYNAMIC_THREADS );
  588. if ((Queue->QueueDepthLastPass > 0) &&
  589. (Queue->WorkItemsProcessed == Queue->WorkItemsProcessedLastPass) &&
  590. (Queue->DynamicThreadCount < MAX_ADDITIONAL_DYNAMIC_THREADS)) {
  591. //
  592. // These things are known:
  593. //
  594. // - There were work items waiting in the queue at the last pass.
  595. // - No work items have been processed since the last pass.
  596. // - We haven't yet created the maximum number of dynamic threads.
  597. //
  598. // Things look like they're stuck, create a new thread for this
  599. // queue.
  600. //
  601. // We explicitly ignore an error from ExpCreateDynamicThread():
  602. // we'll try again in another detection period if the queue looks
  603. // like it's still stuck.
  604. //
  605. ExpCreateWorkerThread (Index, TRUE);
  606. }
  607. //
  608. // Update some bookkeeping.
  609. //
  610. // Note that WorkItemsProcessed and the queue depth must be recorded
  611. // in that order to avoid getting a false deadlock indication.
  612. //
  613. Queue->WorkItemsProcessedLastPass = Queue->WorkItemsProcessed;
  614. Queue->QueueDepthLastPass = KeReadStateQueue (&Queue->WorkerQueue);
  615. }
  616. }
  617. NTSTATUS
  618. ExpCreateWorkerThread (
  619. IN WORK_QUEUE_TYPE QueueType,
  620. IN BOOLEAN Dynamic
  621. )
  622. /*++
  623. Routine Description:
  624. This function creates a single new static or dynamic worker thread for
  625. the given queue type.
  626. Arguments:
  627. QueueType - Supplies the type of the queue for which the worker thread
  628. should be created.
  629. Dynamic - If TRUE, the worker thread is created as a dynamic thread that
  630. will terminate after a sufficient period of inactivity. If FALSE,
  631. the worker thread will never terminate.
  632. Return Value:
  633. The final status of the operation.
  634. Notes:
  635. This routine is only called from the worker thread set balance thread,
  636. therefore it will not be reentered.
  637. --*/
  638. {
  639. OBJECT_ATTRIBUTES ObjectAttributes;
  640. NTSTATUS Status;
  641. HANDLE ThreadHandle;
  642. ULONG Context;
  643. ULONG BasePriority;
  644. PETHREAD Thread;
  645. InitializeObjectAttributes (&ObjectAttributes, NULL, 0, NULL, NULL);
  646. Context = QueueType;
  647. if (Dynamic != FALSE) {
  648. Context |= DYNAMIC_WORKER_THREAD;
  649. }
  650. Status = PsCreateSystemThread (&ThreadHandle,
  651. THREAD_ALL_ACCESS,
  652. &ObjectAttributes,
  653. 0L,
  654. NULL,
  655. ExpWorkerThread,
  656. (PVOID)(ULONG_PTR)Context);
  657. if (!NT_SUCCESS(Status)) {
  658. return Status;
  659. }
  660. if (Dynamic != FALSE) {
  661. InterlockedIncrement ((PLONG)&ExWorkerQueue[QueueType].DynamicThreadCount);
  662. }
  663. //
  664. // Set the priority according to the type of worker thread.
  665. //
  666. switch (QueueType) {
  667. case HyperCriticalWorkQueue:
  668. BasePriority = HYPER_CRITICAL_WORK_QUEUE_PRIORITY;
  669. break;
  670. case CriticalWorkQueue:
  671. BasePriority = CRITICAL_WORK_QUEUE_PRIORITY;
  672. break;
  673. case DelayedWorkQueue:
  674. default:
  675. BasePriority = DELAYED_WORK_QUEUE_PRIORITY;
  676. break;
  677. }
  678. //
  679. // Set the base priority of the just-created thread.
  680. //
  681. Status = ObReferenceObjectByHandle (ThreadHandle,
  682. THREAD_SET_INFORMATION,
  683. PsThreadType,
  684. KernelMode,
  685. (PVOID *)&Thread,
  686. NULL);
  687. if (NT_SUCCESS(Status)) {
  688. KeSetBasePriorityThread (&Thread->Tcb, BasePriority);
  689. ObDereferenceObject (Thread);
  690. }
  691. ZwClose (ThreadHandle);
  692. return Status;
  693. }
  694. VOID
  695. ExpCheckForWorker (
  696. IN PVOID p,
  697. IN SIZE_T Size
  698. )
  699. {
  700. KIRQL OldIrql;
  701. PLIST_ENTRY Entry;
  702. PCHAR BeginBlock;
  703. PCHAR EndBlock;
  704. WORK_QUEUE_TYPE wqt;
  705. BeginBlock = (PCHAR)p;
  706. EndBlock = (PCHAR)p + Size;
  707. KiLockDispatcherDatabase (&OldIrql);
  708. for (wqt = CriticalWorkQueue; wqt < MaximumWorkQueue; wqt += 1) {
  709. for (Entry = (PLIST_ENTRY) ExWorkerQueue[wqt].WorkerQueue.EntryListHead.Flink;
  710. Entry && (Entry != (PLIST_ENTRY) &ExWorkerQueue[wqt].WorkerQueue.EntryListHead);
  711. Entry = Entry->Flink) {
  712. if (((PCHAR) Entry >= BeginBlock) && ((PCHAR) Entry < EndBlock)) {
  713. KeBugCheckEx(WORKER_INVALID,
  714. 0x0,
  715. (ULONG_PTR)Entry,
  716. (ULONG_PTR)BeginBlock,
  717. (ULONG_PTR)EndBlock);
  718. }
  719. }
  720. }
  721. KiUnlockDispatcherDatabase (OldIrql);
  722. }
  723. #ifdef ALLOC_DATA_PRAGMA
  724. #pragma const_seg("PAGECONST")
  725. #endif
  726. const char ExpWorkerApcDisabledMessage[] =
  727. "EXWORKER: worker exit with APCs disabled, worker routine %x, parameter %x, item %x\n";
  728. #ifdef ALLOC_DATA_PRAGMA
  729. #pragma const_seg()
  730. #endif
  731. VOID
  732. ExpWorkerThread (
  733. IN PVOID StartContext
  734. )
  735. {
  736. PLIST_ENTRY Entry;
  737. WORK_QUEUE_TYPE QueueType;
  738. PWORK_QUEUE_ITEM WorkItem;
  739. KPROCESSOR_MODE WaitMode;
  740. LARGE_INTEGER TimeoutValue;
  741. PLARGE_INTEGER Timeout;
  742. PETHREAD Thread;
  743. PEX_WORK_QUEUE WorkerQueue;
  744. PWORKER_THREAD_ROUTINE WorkerRoutine;
  745. PVOID Parameter;
  746. EX_QUEUE_WORKER_INFO OldWorkerInfo;
  747. EX_QUEUE_WORKER_INFO NewWorkerInfo;
  748. ULONG CountForQueueEmpty;
  749. //
  750. // Set timeout value etc according to whether we are static or dynamic.
  751. //
  752. if (((ULONG_PTR)StartContext & DYNAMIC_WORKER_THREAD) == 0) {
  753. //
  754. // We are being created as a static thread. As such it will not
  755. // terminate, so there is no point in timing out waiting for a work
  756. // item.
  757. //
  758. Timeout = NULL;
  759. }
  760. else {
  761. //
  762. // This is a dynamic worker thread. It has a non-infinite timeout
  763. // so that it can eventually terminate.
  764. //
  765. TimeoutValue.QuadPart = -DYNAMIC_THREAD_TIMEOUT;
  766. Timeout = &TimeoutValue;
  767. }
  768. Thread = PsGetCurrentThread ();
  769. //
  770. // If the thread is a critical worker thread, then set the thread
  771. // priority to the lowest realtime level. Otherwise, set the base
  772. // thread priority to time critical.
  773. //
  774. QueueType = (WORK_QUEUE_TYPE)
  775. ((ULONG_PTR)StartContext & ~DYNAMIC_WORKER_THREAD);
  776. WorkerQueue = &ExWorkerQueue[QueueType];
  777. WaitMode = (KPROCESSOR_MODE) WorkerQueue->Info.WaitMode;
  778. ASSERT (Thread->ExWorkerCanWaitUser == 0);
  779. if (WaitMode == UserMode) {
  780. Thread->ExWorkerCanWaitUser = 1;
  781. }
  782. #if defined(REMOTE_BOOT)
  783. //
  784. // In diskless NT scenarios ensure that the kernel stack of the worker
  785. // threads will not be swapped out.
  786. //
  787. if (IoRemoteBootClient) {
  788. KeSetKernelStackSwapEnable (FALSE);
  789. }
  790. #endif // defined(REMOTE_BOOT)
  791. //
  792. // Register as a worker, exiting if the queue's going down and
  793. // there aren't any workers in the queue to hand us the shutdown
  794. // work item if we enter the queue (we want to be able to enter a
  795. // queue even if the queue's shutting down, in case there's a
  796. // backlog of work items that the balance manager thread's decided
  797. // we should be helping to process).
  798. //
  799. if (PO_SHUTDOWN_QUEUE == QueueType) {
  800. CountForQueueEmpty = 1;
  801. }
  802. else {
  803. CountForQueueEmpty = 0;
  804. }
  805. if (ExpWorkersCanSwap == FALSE) {
  806. KeSetKernelStackSwapEnable (FALSE);
  807. }
  808. do {
  809. OldWorkerInfo.QueueWorkerInfo = WorkerQueue->Info.QueueWorkerInfo;
  810. if (OldWorkerInfo.QueueDisabled &&
  811. OldWorkerInfo.WorkerCount <= CountForQueueEmpty) {
  812. //
  813. // The queue is disabled and empty so just exit.
  814. //
  815. KeSetKernelStackSwapEnable (TRUE);
  816. PsTerminateSystemThread (STATUS_SYSTEM_SHUTDOWN);
  817. }
  818. NewWorkerInfo.QueueWorkerInfo = OldWorkerInfo.QueueWorkerInfo;
  819. NewWorkerInfo.WorkerCount += 1;
  820. } while (OldWorkerInfo.QueueWorkerInfo !=
  821. InterlockedCompareExchange (&WorkerQueue->Info.QueueWorkerInfo,
  822. NewWorkerInfo.QueueWorkerInfo,
  823. OldWorkerInfo.QueueWorkerInfo));
  824. //
  825. // As of this point, we must only exit if we decrement the worker
  826. // count without the queue disabled flag being set. (Unless we
  827. // exit due to the shutdown work item, which also decrements the
  828. // worker count).
  829. //
  830. Thread->ActiveExWorker = 1;
  831. //
  832. // Loop forever waiting for a work queue item, calling the processing
  833. // routine, and then waiting for another work queue item.
  834. //
  835. do {
  836. //
  837. // Wait until something is put in the queue or until we time out.
  838. //
  839. // By specifying a wait mode of UserMode, the thread's kernel
  840. // stack is swappable.
  841. //
  842. Entry = KeRemoveQueue (&WorkerQueue->WorkerQueue,
  843. WaitMode,
  844. Timeout);
  845. if ((ULONG_PTR)Entry != STATUS_TIMEOUT) {
  846. //
  847. // This is a real work item, process it.
  848. //
  849. // Update the total number of work items processed.
  850. //
  851. InterlockedIncrement ((PLONG)&WorkerQueue->WorkItemsProcessed);
  852. WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List);
  853. WorkerRoutine = WorkItem->WorkerRoutine;
  854. Parameter = WorkItem->Parameter;
  855. //
  856. // Execute the specified routine.
  857. //
  858. ((PWORKER_THREAD_ROUTINE)WorkerRoutine) (Parameter);
  859. #if DBG
  860. if (IsListEmpty (&Thread->IrpList)) {
  861. //
  862. // See if a worker just returned while holding a resource
  863. //
  864. ExCheckIfResourceOwned ();
  865. }
  866. #endif
  867. //
  868. // Catch worker routines that forget to leave a critial/guarded
  869. // region. In the debug case execute a breakpoint. In the free
  870. // case zero the flag so that APCs can continue to fire to this
  871. // thread.
  872. //
  873. if (Thread->Tcb.CombinedApcDisable != 0) {
  874. DbgPrint ((char*)ExpWorkerApcDisabledMessage,
  875. WorkerRoutine,
  876. Parameter,
  877. WorkItem);
  878. ASSERT (FALSE);
  879. Thread->Tcb.CombinedApcDisable = 0;
  880. }
  881. if (KeGetCurrentIrql () != PASSIVE_LEVEL) {
  882. KeBugCheckEx (WORKER_THREAD_RETURNED_AT_BAD_IRQL,
  883. (ULONG_PTR)WorkerRoutine,
  884. (ULONG_PTR)KeGetCurrentIrql(),
  885. (ULONG_PTR)Parameter,
  886. (ULONG_PTR)WorkItem);
  887. }
  888. if (PS_IS_THREAD_IMPERSONATING (Thread)) {
  889. KeBugCheckEx (IMPERSONATING_WORKER_THREAD,
  890. (ULONG_PTR)WorkerRoutine,
  891. (ULONG_PTR)Parameter,
  892. (ULONG_PTR)WorkItem,
  893. 0);
  894. }
  895. continue;
  896. }
  897. //
  898. // These things are known:
  899. //
  900. // - Static worker threads do not time out, so this is a dynamic
  901. // worker thread.
  902. //
  903. // - This thread has been waiting for a long time with nothing
  904. // to do.
  905. //
  906. if (IsListEmpty (&Thread->IrpList) == FALSE) {
  907. //
  908. // There is still I/O pending, can't terminate yet.
  909. //
  910. continue;
  911. }
  912. //
  913. // Get out of the queue, if we can
  914. //
  915. do {
  916. OldWorkerInfo.QueueWorkerInfo = WorkerQueue->Info.QueueWorkerInfo;
  917. if (OldWorkerInfo.QueueDisabled) {
  918. //
  919. // We're exiting via the queue disable work item;
  920. // there's no point in expiring here.
  921. //
  922. break;
  923. }
  924. NewWorkerInfo.QueueWorkerInfo = OldWorkerInfo.QueueWorkerInfo;
  925. NewWorkerInfo.WorkerCount -= 1;
  926. } while (OldWorkerInfo.QueueWorkerInfo
  927. != InterlockedCompareExchange(&WorkerQueue->Info.QueueWorkerInfo,
  928. NewWorkerInfo.QueueWorkerInfo,
  929. OldWorkerInfo.QueueWorkerInfo));
  930. if (OldWorkerInfo.QueueDisabled) {
  931. //
  932. // We're exiting via the queue disable work item
  933. //
  934. continue;
  935. }
  936. //
  937. // This dynamic thread can be terminated.
  938. //
  939. break;
  940. } while (TRUE);
  941. //
  942. // Terminate this dynamic thread.
  943. //
  944. InterlockedDecrement ((PLONG)&WorkerQueue->DynamicThreadCount);
  945. //
  946. // Carefully clear this before marking the thread stack as swap enabled
  947. // so that an incoming APC won't inadvertently disable the stack swap
  948. // afterwards.
  949. //
  950. Thread->ActiveExWorker = 0;
  951. //
  952. // We will bugcheck if we terminate a thread with stack swapping
  953. // disabled.
  954. //
  955. KeSetKernelStackSwapEnable (TRUE);
  956. return;
  957. }
  958. VOID
  959. ExpSetSwappingKernelApc (
  960. IN PKAPC Apc,
  961. OUT PKNORMAL_ROUTINE *NormalRoutine,
  962. IN OUT PVOID NormalContext,
  963. IN OUT PVOID *SystemArgument1,
  964. IN OUT PVOID *SystemArgument2
  965. )
  966. {
  967. PBOOLEAN AllowSwap;
  968. PKEVENT SwapSetEvent;
  969. UNREFERENCED_PARAMETER (Apc);
  970. UNREFERENCED_PARAMETER (NormalRoutine);
  971. UNREFERENCED_PARAMETER (SystemArgument2);
  972. //
  973. // SystemArgument1 is a pointer to the event to signal once this
  974. // thread has finished servicing the request.
  975. //
  976. SwapSetEvent = (PKEVENT) *SystemArgument1;
  977. //
  978. // Don't disable stack swapping if the thread is exiting because
  979. // it cannot exit this way without bugchecking. Skip it on enables
  980. // too since the thread is bailing anyway.
  981. //
  982. if (PsGetCurrentThread()->ActiveExWorker != 0) {
  983. AllowSwap = NormalContext;
  984. KeSetKernelStackSwapEnable (*AllowSwap);
  985. }
  986. KeSetEvent (SwapSetEvent, 0, FALSE);
  987. }
  988. VOID
  989. ExSwapinWorkerThreads (
  990. IN BOOLEAN AllowSwap
  991. )
  992. /*++
  993. Routine Description:
  994. Sets the kernel stacks of the delayed worker threads to be swappable
  995. or pins them into memory.
  996. Arguments:
  997. AllowSwap - Supplies TRUE if worker kernel stacks should be swappable,
  998. FALSE if not.
  999. Return Value:
  1000. None.
  1001. --*/
  1002. {
  1003. PETHREAD Thread;
  1004. PETHREAD CurrentThread;
  1005. PEPROCESS Process;
  1006. KAPC Apc;
  1007. KEVENT SwapSetEvent;
  1008. PAGED_CODE();
  1009. CurrentThread = PsGetCurrentThread();
  1010. KeInitializeEvent (&SwapSetEvent,
  1011. NotificationEvent,
  1012. FALSE);
  1013. Process = PsInitialSystemProcess;
  1014. //
  1015. // Serialize callers.
  1016. //
  1017. ExAcquireFastMutex (&ExpWorkerSwapinMutex);
  1018. //
  1019. // Stop new threads from swapping.
  1020. //
  1021. ExpWorkersCanSwap = AllowSwap;
  1022. //
  1023. // Stop existing worker threads from swapping.
  1024. //
  1025. for (Thread = PsGetNextProcessThread (Process, NULL);
  1026. Thread != NULL;
  1027. Thread = PsGetNextProcessThread (Process, Thread)) {
  1028. //
  1029. // Skip threads that are not worker threads or worker threads that
  1030. // were permanently marked noswap at creation time.
  1031. //
  1032. if (Thread->ExWorkerCanWaitUser == 0) {
  1033. continue;
  1034. }
  1035. if (Thread == CurrentThread) {
  1036. //
  1037. // No need to use an APC on the current thread.
  1038. //
  1039. KeSetKernelStackSwapEnable (AllowSwap);
  1040. }
  1041. else {
  1042. //
  1043. // Queue an APC to the thread, and wait for it to fire:
  1044. //
  1045. KeInitializeApc (&Apc,
  1046. &Thread->Tcb,
  1047. InsertApcEnvironment,
  1048. ExpSetSwappingKernelApc,
  1049. NULL,
  1050. NULL,
  1051. KernelMode,
  1052. &AllowSwap);
  1053. if (KeInsertQueueApc (&Apc, &SwapSetEvent, NULL, 3)) {
  1054. KeWaitForSingleObject (&SwapSetEvent,
  1055. Executive,
  1056. KernelMode,
  1057. FALSE,
  1058. NULL);
  1059. KeClearEvent(&SwapSetEvent);
  1060. }
  1061. }
  1062. }
  1063. ExReleaseFastMutex (&ExpWorkerSwapinMutex);
  1064. }
  1065. LOGICAL
  1066. ExpCheckQueueShutdown (
  1067. IN WORK_QUEUE_TYPE QueueType,
  1068. IN PSHUTDOWN_WORK_ITEM ShutdownItem
  1069. )
  1070. {
  1071. ULONG CountForQueueEmpty;
  1072. if (PO_SHUTDOWN_QUEUE == QueueType) {
  1073. CountForQueueEmpty = 1;
  1074. }
  1075. else {
  1076. CountForQueueEmpty = 0;
  1077. }
  1078. //
  1079. // Note that using interlocked sequences to increment the worker count
  1080. // and decrement it to CountForQueueEmpty ensures that once it
  1081. // *is* equal to CountForQueueEmpty and the disabled flag is set,
  1082. // we won't be incrementing it any more, so we're safe making this
  1083. // check without locks.
  1084. //
  1085. // See ExpWorkerThread, ExpShutdownWorker, and ExpShutdownWorkerThreads.
  1086. //
  1087. if (ExWorkerQueue[QueueType].Info.WorkerCount > CountForQueueEmpty) {
  1088. //
  1089. // There're still worker threads; send one of them the axe.
  1090. //
  1091. ShutdownItem->QueueType = QueueType;
  1092. ShutdownItem->PrevThread = PsGetCurrentThread();
  1093. ObReferenceObject (ShutdownItem->PrevThread);
  1094. KeInsertQueue (&ExWorkerQueue[QueueType].WorkerQueue,
  1095. &ShutdownItem->WorkItem.List);
  1096. return TRUE;
  1097. }
  1098. return FALSE; // we did not queue a shutdown
  1099. }
  1100. VOID
  1101. ExpShutdownWorker (
  1102. IN PVOID Parameter
  1103. )
  1104. {
  1105. PETHREAD CurrentThread;
  1106. PSHUTDOWN_WORK_ITEM ShutdownItem;
  1107. ShutdownItem = (PSHUTDOWN_WORK_ITEM) Parameter;
  1108. ASSERT (ShutdownItem != NULL);
  1109. if (ShutdownItem->PrevThread != NULL) {
  1110. //
  1111. // Wait for the previous thread to exit -- if it's in the same
  1112. // queue, it probably has already, but we need to make sure
  1113. // (and if it's not, we *definitely* need to make sure).
  1114. //
  1115. KeWaitForSingleObject (ShutdownItem->PrevThread,
  1116. Executive,
  1117. KernelMode,
  1118. FALSE,
  1119. NULL);
  1120. ObDereferenceObject (ShutdownItem->PrevThread);
  1121. ShutdownItem->PrevThread = NULL;
  1122. }
  1123. //
  1124. // Decrement the worker count.
  1125. //
  1126. InterlockedDecrement (&ExWorkerQueue[ShutdownItem->QueueType].Info.QueueWorkerInfo);
  1127. CurrentThread = PsGetCurrentThread();
  1128. if ((!ExpCheckQueueShutdown(DelayedWorkQueue, ShutdownItem)) &&
  1129. (!ExpCheckQueueShutdown(CriticalWorkQueue, ShutdownItem))) {
  1130. //
  1131. // We're the last worker to exit
  1132. //
  1133. ASSERT (!ExpLastWorkerThread);
  1134. ExpLastWorkerThread = CurrentThread;
  1135. ObReferenceObject (ExpLastWorkerThread);
  1136. KeSetEvent (&ExpThreadSetManagerShutdownEvent, 0, FALSE);
  1137. }
  1138. KeSetKernelStackSwapEnable (TRUE);
  1139. CurrentThread->ActiveExWorker = 0;
  1140. PsTerminateSystemThread (STATUS_SYSTEM_SHUTDOWN);
  1141. }
  1142. VOID
  1143. ExpShutdownWorkerThreads (
  1144. VOID
  1145. )
  1146. {
  1147. PULONG QueueEnable;
  1148. SHUTDOWN_WORK_ITEM ShutdownItem;
  1149. if ((PoCleanShutdownEnabled () & PO_CLEAN_SHUTDOWN_WORKERS) == 0) {
  1150. return;
  1151. }
  1152. ASSERT (KeGetCurrentThread()->Queue
  1153. == &ExWorkerQueue[PO_SHUTDOWN_QUEUE].WorkerQueue);
  1154. //
  1155. // Mark the queues as terminating.
  1156. //
  1157. QueueEnable = (PULONG)&ExWorkerQueue[DelayedWorkQueue].Info.QueueWorkerInfo;
  1158. RtlInterlockedSetBitsDiscardReturn (QueueEnable, EX_WORKER_QUEUE_DISABLED);
  1159. QueueEnable = (PULONG)&ExWorkerQueue[CriticalWorkQueue].Info.QueueWorkerInfo;
  1160. RtlInterlockedSetBitsDiscardReturn (QueueEnable, EX_WORKER_QUEUE_DISABLED);
  1161. //
  1162. // Queue the shutdown work item to the delayed work queue. After
  1163. // all currently queued work items are complete, this will fire,
  1164. // repeatedly taking out every worker thread in every queue until
  1165. // they're all done.
  1166. //
  1167. ExInitializeWorkItem (&ShutdownItem.WorkItem,
  1168. &ExpShutdownWorker,
  1169. &ShutdownItem);
  1170. ShutdownItem.QueueType = DelayedWorkQueue;
  1171. ShutdownItem.PrevThread = NULL;
  1172. KeInsertQueue (&ExWorkerQueue[DelayedWorkQueue].WorkerQueue,
  1173. &ShutdownItem.WorkItem.List);
  1174. //
  1175. // Wait for all of the workers and the balancer to exit.
  1176. //
  1177. if (ExpWorkerThreadBalanceManagerPtr != NULL) {
  1178. KeWaitForSingleObject(ExpWorkerThreadBalanceManagerPtr,
  1179. Executive,
  1180. KernelMode,
  1181. FALSE,
  1182. NULL);
  1183. ASSERT(!ShutdownItem.PrevThread);
  1184. ObDereferenceObject(ExpWorkerThreadBalanceManagerPtr);
  1185. }
  1186. }
  1187. VOID
  1188. ExpDebuggerWorker(
  1189. IN PVOID Context
  1190. )
  1191. /*++
  1192. Routine Description:
  1193. This is a worker thread for the kernel debugger that can be used to
  1194. perform certain tasks on the target machine asynchronously.
  1195. This is necessary when the machine needs to run at Dispatch level to
  1196. perform certain operations, such as paging in data.
  1197. Arguments:
  1198. Context - not used as this point.
  1199. Return Value:
  1200. None.
  1201. --*/
  1202. {
  1203. NTSTATUS Status;
  1204. KAPC_STATE ApcState;
  1205. volatile UCHAR Data;
  1206. PRKPROCESS KillProcess = (PRKPROCESS) ExpDebuggerProcessKill;
  1207. PRKPROCESS AttachProcess = (PRKPROCESS) ExpDebuggerProcessAttach;
  1208. PUCHAR PageIn = (PUCHAR) ExpDebuggerPageIn;
  1209. PEPROCESS Process;
  1210. ExpDebuggerProcessKill = 0;
  1211. ExpDebuggerProcessAttach = 0;
  1212. ExpDebuggerPageIn = 0;
  1213. UNREFERENCED_PARAMETER (Context);
  1214. #if DBG
  1215. if (ExpDebuggerWork != 2)
  1216. {
  1217. DbgPrint("ExpDebuggerWorker being entered with state != 2\n");
  1218. }
  1219. #endif
  1220. ExpDebuggerWork = 0;
  1221. Process = NULL;
  1222. if (AttachProcess || KillProcess) {
  1223. for (Process = PsGetNextProcess (NULL);
  1224. Process != NULL;
  1225. Process = PsGetNextProcess (Process)) {
  1226. if (&Process->Pcb == AttachProcess) {
  1227. KeStackAttachProcess (AttachProcess, &ApcState);
  1228. break;
  1229. }
  1230. if (&Process->Pcb == KillProcess) {
  1231. PsTerminateProcess(Process, DBG_TERMINATE_PROCESS);
  1232. PsQuitNextProcess (Process);
  1233. return;
  1234. }
  1235. }
  1236. }
  1237. if (PageIn) {
  1238. try {
  1239. ProbeForReadSmallStructure (PageIn, sizeof (UCHAR), sizeof (UCHAR));
  1240. Data = *PageIn;
  1241. } except (EXCEPTION_EXECUTE_HANDLER) {
  1242. Status = GetExceptionCode();
  1243. }
  1244. }
  1245. DbgBreakPointWithStatus(DBG_STATUS_WORKER);
  1246. if (Process != NULL) {
  1247. KeUnstackDetachProcess (&ApcState);
  1248. PsQuitNextProcess (Process);
  1249. }
  1250. return;
  1251. }