/*++ * @name ExpCheckDynamicThreadCount * * The ExpCheckDynamicThreadCount routine checks every queue and creates a * dynamic thread if the queue requires one. * * @param None * * @return None. * * @remarks The algorithm for deciding if a new thread must be created is * documented in the ExQueueWorkItem routine. * *--*/ VOID NTAPI ExpCheckDynamicThreadCount(VOID) { ULONG i; PEX_WORK_QUEUE Queue; /* Loop the 3 queues */ for (i = 0; i < MaximumWorkQueue; i++) { /* Get the queue */ Queue = &ExWorkerQueue[i]; /* Check if still need a new thread. See ExQueueWorkItem */ if ((Queue->Info.MakeThreadsAsNecessary) && (!IsListEmpty(&Queue->WorkerQueue.EntryListHead)) && (Queue->WorkerQueue.CurrentCount < Queue->WorkerQueue.MaximumCount) && (Queue->DynamicThreadCount < 16)) { /* Create a new thread */ DPRINT1("EX: Creating new dynamic thread as requested\n"); ExpCreateWorkerThread(i, TRUE); } } }
/*++ * @name ExpCheckDynamicThreadCount * * The ExpCheckDynamicThreadCount routine checks every queue and creates a * dynamic thread if the queue seems to be deadlocked. * * @param None * * @return None. * * @remarks The algorithm for deciding if a new thread must be created is * based on wether the queue has processed no new items in the last * second, and new items are still enqueued. * *--*/ VOID NTAPI ExpDetectWorkerThreadDeadlock(VOID) { ULONG i; PEX_WORK_QUEUE Queue; /* Loop the 3 queues */ for (i = 0; i < MaximumWorkQueue; i++) { /* Get the queue */ Queue = &ExWorkerQueue[i]; ASSERT(Queue->DynamicThreadCount <= 16); /* Check if stuff is on the queue that still is unprocessed */ if ((Queue->QueueDepthLastPass) && (Queue->WorkItemsProcessed == Queue->WorkItemsProcessedLastPass) && (Queue->DynamicThreadCount < 16)) { /* Stuff is still on the queue and nobody did anything about it */ DPRINT1("EX: Work Queue Deadlock detected: %d\n", i); ExpCreateWorkerThread(i, TRUE); DPRINT1("Dynamic threads queued %d\n", Queue->DynamicThreadCount); } /* Update our data */ Queue->WorkItemsProcessedLastPass = Queue->WorkItemsProcessed; Queue->QueueDepthLastPass = KeReadStateQueue(&Queue->WorkerQueue); } }
VOID ExpCheckDynamicThreadCount ( VOID ) /*++ Routine Description: This routine is called when there is reason to believe that a work queue might benefit from the creation of an additional worker thread. This routine checks each queue to determine whether it would benefit from an additional worker thread (see ExpNewThreadNecessary()), and creates one if so. Arguments: None. Return Value: None. --*/ { PEX_WORK_QUEUE Queue; WORK_QUEUE_TYPE QueueType; PAGED_CODE(); // // Check each worker queue. // Queue = &ExWorkerQueue[0]; for (QueueType = 0; QueueType < MaximumWorkQueue; Queue += 1, QueueType += 1) { if (ExpNewThreadNecessary (Queue)) { // // Create a new thread for this queue. We explicitly ignore // an error from ExpCreateDynamicThread(): there's nothing // we can or should do in the event of a failure. // ExpCreateWorkerThread (QueueType, TRUE); } } }
/*++ * @name ExpInitializeWorkerThreads * * The ExpInitializeWorkerThreads routine initializes worker thread and * work queue support. * * @param None. * * @return None. * * @remarks This routine is only called once during system initialization. * *--*/ VOID INIT_FUNCTION NTAPI ExpInitializeWorkerThreads(VOID) { ULONG WorkQueueType; ULONG CriticalThreads, DelayedThreads; HANDLE ThreadHandle; PETHREAD Thread; ULONG i; /* Setup the stack swap support */ ExInitializeFastMutex(&ExpWorkerSwapinMutex); InitializeListHead(&ExpWorkerListHead); ExpWorkersCanSwap = TRUE; /* Set the number of critical and delayed threads. We shouldn't hardcode */ DelayedThreads = EX_DELAYED_WORK_THREADS; CriticalThreads = EX_CRITICAL_WORK_THREADS; /* Protect against greedy registry modifications */ ExpAdditionalDelayedWorkerThreads = min(ExpAdditionalDelayedWorkerThreads, 16); ExpAdditionalCriticalWorkerThreads = min(ExpAdditionalCriticalWorkerThreads, 16); /* Calculate final count */ DelayedThreads += ExpAdditionalDelayedWorkerThreads; CriticalThreads += ExpAdditionalCriticalWorkerThreads; /* Initialize the Array */ for (WorkQueueType = 0; WorkQueueType < MaximumWorkQueue; WorkQueueType++) { /* Clear the structure and initialize the queue */ RtlZeroMemory(&ExWorkerQueue[WorkQueueType], sizeof(EX_WORK_QUEUE)); KeInitializeQueue(&ExWorkerQueue[WorkQueueType].WorkerQueue, 0); } /* Dynamic threads are only used for the critical queue */ ExWorkerQueue[CriticalWorkQueue].Info.MakeThreadsAsNecessary = TRUE; /* Initialize the balance set manager events */ KeInitializeEvent(&ExpThreadSetManagerEvent, SynchronizationEvent, FALSE); KeInitializeEvent(&ExpThreadSetManagerShutdownEvent, NotificationEvent, FALSE); /* Create the built-in worker threads for the critical queue */ for (i = 0; i < CriticalThreads; i++) { /* Create the thread */ ExpCreateWorkerThread(CriticalWorkQueue, FALSE); ExpCriticalWorkerThreads++; } /* Create the built-in worker threads for the delayed queue */ for (i = 0; i < DelayedThreads; i++) { /* Create the thread */ ExpCreateWorkerThread(DelayedWorkQueue, FALSE); ExpDelayedWorkerThreads++; } /* Create the built-in worker thread for the hypercritical queue */ ExpCreateWorkerThread(HyperCriticalWorkQueue, FALSE); /* Create the balance set manager thread */ PsCreateSystemThread(&ThreadHandle, THREAD_ALL_ACCESS, NULL, 0, NULL, ExpWorkerThreadBalanceManager, NULL); /* Get a pointer to it for the shutdown process */ ObReferenceObjectByHandle(ThreadHandle, THREAD_ALL_ACCESS, NULL, KernelMode, (PVOID*)&Thread, NULL); ExpWorkerThreadBalanceManagerPtr = Thread; /* Close the handle and return */ ObCloseHandle(ThreadHandle, KernelMode); }
VOID ExpDetectWorkerThreadDeadlock ( VOID ) /*++ Routine Description: This function creates new work item threads if a possible deadlock is detected. Arguments: None. Return Value: None --*/ { ULONG Index; PEX_WORK_QUEUE Queue; PAGED_CODE(); // // Process each queue type. // for (Index = 0; Index < MaximumWorkQueue; Index += 1) { Queue = &ExWorkerQueue[Index]; ASSERT( Queue->DynamicThreadCount <= MAX_ADDITIONAL_DYNAMIC_THREADS ); if ((Queue->QueueDepthLastPass > 0) && (Queue->WorkItemsProcessed == Queue->WorkItemsProcessedLastPass) && (Queue->DynamicThreadCount < MAX_ADDITIONAL_DYNAMIC_THREADS)) { // // These things are known: // // - There were work items waiting in the queue at the last pass. // - No work items have been processed since the last pass. // - We haven't yet created the maximum number of dynamic threads. // // Things look like they're stuck, create a new thread for this // queue. // // We explicitly ignore an error from ExpCreateDynamicThread(): // we'll try again in another detection period if the queue looks // like it's still stuck. // ExpCreateWorkerThread (Index, TRUE); } // // Update some bookkeeping. // // Note that WorkItemsProcessed and the queue depth must be recorded // in that order to avoid getting a false deadlock indication. // Queue->WorkItemsProcessedLastPass = Queue->WorkItemsProcessed; Queue->QueueDepthLastPass = KeReadStateQueue (&Queue->WorkerQueue); } }
NTSTATUS ExpWorkerInitialization ( VOID ) { ULONG Index; OBJECT_ATTRIBUTES ObjectAttributes; ULONG NumberOfDelayedThreads; ULONG NumberOfCriticalThreads; ULONG NumberOfThreads; NTSTATUS Status; HANDLE Thread; BOOLEAN NtAs; WORK_QUEUE_TYPE WorkQueueType; ExInitializeFastMutex (&ExpWorkerSwapinMutex); InitializeListHead (&ExpWorkerListHead); ExpWorkersCanSwap = TRUE; // // Set the number of worker threads based on the system size. // NtAs = MmIsThisAnNtAsSystem(); NumberOfCriticalThreads = MEDIUM_NUMBER_OF_THREADS; // // Incremented boot time number of delayed threads. // We did this in Windows XP, because 3COM NICs would take a long // time with the network stack tying up the delayed worker threads. // When Mm would need a worker thread to load a driver on the critical // path of boot, it would also get stuck for a few seconds and hurt // boot times. Ideally we'd spawn new delayed threads as necessary as // well to prevent such contention from hurting boot and resume. // NumberOfDelayedThreads = MEDIUM_NUMBER_OF_THREADS + 4; switch (MmQuerySystemSize()) { case MmSmallSystem: break; case MmMediumSystem: if (NtAs) { NumberOfCriticalThreads += MEDIUM_NUMBER_OF_THREADS; } break; case MmLargeSystem: NumberOfCriticalThreads = LARGE_NUMBER_OF_THREADS; if (NtAs) { NumberOfCriticalThreads += LARGE_NUMBER_OF_THREADS; } break; default: break; } // // Initialize the work Queue objects. // if (ExpAdditionalCriticalWorkerThreads > MAX_ADDITIONAL_THREADS) { ExpAdditionalCriticalWorkerThreads = MAX_ADDITIONAL_THREADS; } if (ExpAdditionalDelayedWorkerThreads > MAX_ADDITIONAL_THREADS) { ExpAdditionalDelayedWorkerThreads = MAX_ADDITIONAL_THREADS; } // // Initialize the ExWorkerQueue[] array. // RtlZeroMemory (&ExWorkerQueue[0], MaximumWorkQueue * sizeof(EX_WORK_QUEUE)); for (WorkQueueType = 0; WorkQueueType < MaximumWorkQueue; WorkQueueType += 1) { KeInitializeQueue (&ExWorkerQueue[WorkQueueType].WorkerQueue, 0); ExWorkerQueue[WorkQueueType].Info.WaitMode = UserMode; } // // Always make stack for this thread resident // so that worker pool deadlock magic can run // even when what we are trying to do is inpage // the hyper critical worker thread's stack. // Without this fix, we hold the process lock // but this thread's stack can't come in, and // the deadlock detection cannot create new threads // to break the system deadlock. // ExWorkerQueue[HyperCriticalWorkQueue].Info.WaitMode = KernelMode; if (NtAs) { ExWorkerQueue[CriticalWorkQueue].Info.WaitMode = KernelMode; } // // We only create dynamic threads for the critical work queue (note // this doesn't apply to dynamic threads created to break deadlocks.) // // The rationale is this: folks who use the delayed work queue are // not time critical, and the hypercritical queue is used rarely // by folks who are non-blocking. // ExWorkerQueue[CriticalWorkQueue].Info.MakeThreadsAsNecessary = 1; // // Initialize the global thread set manager events // KeInitializeEvent (&ExpThreadSetManagerEvent, SynchronizationEvent, FALSE); KeInitializeEvent (&ExpThreadSetManagerShutdownEvent, SynchronizationEvent, FALSE); // // Create the desired number of executive worker threads for each // of the work queues. // // // Create the builtin critical worker threads. // NumberOfThreads = NumberOfCriticalThreads + ExpAdditionalCriticalWorkerThreads; for (Index = 0; Index < NumberOfThreads; Index += 1) { // // Create a worker thread to service the critical work queue. // Status = ExpCreateWorkerThread (CriticalWorkQueue, FALSE); if (!NT_SUCCESS(Status)) { break; } } ExCriticalWorkerThreads += Index; // // Create the delayed worker threads. // NumberOfThreads = NumberOfDelayedThreads + ExpAdditionalDelayedWorkerThreads; for (Index = 0; Index < NumberOfThreads; Index += 1) { // // Create a worker thread to service the delayed work queue. // Status = ExpCreateWorkerThread (DelayedWorkQueue, FALSE); if (!NT_SUCCESS(Status)) { break; } } ExDelayedWorkerThreads += Index; // // Create the hypercritical worker thread. // Status = ExpCreateWorkerThread (HyperCriticalWorkQueue, FALSE); // // Create the worker thread set manager thread. // InitializeObjectAttributes (&ObjectAttributes, NULL, 0, NULL, NULL); Status = PsCreateSystemThread (&Thread, THREAD_ALL_ACCESS, &ObjectAttributes, 0, NULL, ExpWorkerThreadBalanceManager, NULL); if (NT_SUCCESS(Status)) { Status = ObReferenceObjectByHandle (Thread, SYNCHRONIZE, NULL, KernelMode, &ExpWorkerThreadBalanceManagerPtr, NULL); ZwClose (Thread); } return Status; }