VOID FsRtlWorkerThread( IN PVOID StartContext ) { PLIST_ENTRY Entry; PWORK_QUEUE_ITEM WorkItem; ULONG PagingFile = (ULONG)StartContext; // // Set our priority to low realtime, or +1 for PagingFile. // (PVOID)KeSetPriorityThread( &PsGetCurrentThread()->Tcb, LOW_REALTIME_PRIORITY + PagingFile ); // // Loop forever waiting for a work queue item, calling the processing // routine, and then waiting for another work queue item. // do { // // Wait until something is put in the queue. // // By specifying a wait mode of KernelMode, the thread's kernel stack is // NOT swappable // Entry = KeRemoveQueue(&FsRtlWorkerQueues[PagingFile], KernelMode, NULL); WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List); // // Execute the specified routine. // (WorkItem->WorkerRoutine)(WorkItem->Parameter); if (KeGetCurrentIrql() != 0) { KeBugCheckEx( IRQL_NOT_LESS_OR_EQUAL, (ULONG)WorkItem->WorkerRoutine, (ULONG)KeGetCurrentIrql(), (ULONG)WorkItem->WorkerRoutine, (ULONG)WorkItem ); } } while(TRUE); }
/*++ * @name ExpWorkerThreadEntryPoint * * The ExpWorkerThreadEntryPoint routine is the entrypoint for any new * worker thread created by teh system. * * @param Context * Contains the work queue type masked with a flag specifing whether the * thread is dynamic or not. * * @return None. * * @remarks A dynamic thread can timeout after 10 minutes of waiting on a queue * while a static thread will never timeout. * * Worker threads must return at IRQL == PASSIVE_LEVEL, must not have * active impersonation info, and must not have disabled APCs. * * NB: We will re-enable APCs for broken threads but all other cases * will generate a bugcheck. * *--*/ VOID NTAPI ExpWorkerThreadEntryPoint(IN PVOID Context) { PWORK_QUEUE_ITEM WorkItem; PLIST_ENTRY QueueEntry; WORK_QUEUE_TYPE WorkQueueType; PEX_WORK_QUEUE WorkQueue; LARGE_INTEGER Timeout; PLARGE_INTEGER TimeoutPointer = NULL; PETHREAD Thread = PsGetCurrentThread(); KPROCESSOR_MODE WaitMode; EX_QUEUE_WORKER_INFO OldValue, NewValue; /* Check if this is a dyamic thread */ if ((ULONG_PTR)Context & EX_DYNAMIC_WORK_THREAD) { /* It is, which means we will eventually time out after 10 minutes */ Timeout.QuadPart = Int32x32To64(10, -10000000 * 60); TimeoutPointer = &Timeout; } /* Get Queue Type and Worker Queue */ WorkQueueType = (WORK_QUEUE_TYPE)((ULONG_PTR)Context & ~EX_DYNAMIC_WORK_THREAD); WorkQueue = &ExWorkerQueue[WorkQueueType]; /* Select the wait mode */ WaitMode = (UCHAR)WorkQueue->Info.WaitMode; /* Nobody should have initialized this yet, do it now */ ASSERT(Thread->ExWorkerCanWaitUser == 0); if (WaitMode == UserMode) Thread->ExWorkerCanWaitUser = TRUE; /* If we shouldn't swap, disable that feature */ if (!ExpWorkersCanSwap) KeSetKernelStackSwapEnable(FALSE); /* Set the worker flags */ do { /* Check if the queue is being disabled */ if (WorkQueue->Info.QueueDisabled) { /* Re-enable stack swapping and kill us */ KeSetKernelStackSwapEnable(TRUE); PsTerminateSystemThread(STATUS_SYSTEM_SHUTDOWN); } /* Increase the worker count */ OldValue = WorkQueue->Info; NewValue = OldValue; NewValue.WorkerCount++; } while (InterlockedCompareExchange((PLONG)&WorkQueue->Info, *(PLONG)&NewValue, *(PLONG)&OldValue) != *(PLONG)&OldValue); /* Success, you are now officially a worker thread! */ Thread->ActiveExWorker = TRUE; /* Loop forever */ ProcessLoop: for (;;) { /* Wait for Something to Happen on the Queue */ QueueEntry = KeRemoveQueue(&WorkQueue->WorkerQueue, WaitMode, TimeoutPointer); /* Check if we timed out and quit this loop in that case */ if ((NTSTATUS)(ULONG_PTR)QueueEntry == STATUS_TIMEOUT) break; /* Increment Processed Work Items */ InterlockedIncrement((PLONG)&WorkQueue->WorkItemsProcessed); /* Get the Work Item */ WorkItem = CONTAINING_RECORD(QueueEntry, WORK_QUEUE_ITEM, List); /* Make sure nobody is trying to play smart with us */ ASSERT((ULONG_PTR)WorkItem->WorkerRoutine > MmUserProbeAddress); /* Call the Worker Routine */ WorkItem->WorkerRoutine(WorkItem->Parameter); /* Make sure APCs are not disabled */ if (Thread->Tcb.SpecialApcDisable) { /* We're nice and do it behind your back */ DPRINT1("Warning: Broken Worker Thread: %p %lx %p came back " "with APCs disabled!\n", WorkItem->WorkerRoutine, WorkItem->Parameter, WorkItem); Thread->Tcb.SpecialApcDisable = 0; } /* Make sure it returned at right IRQL */ if (KeGetCurrentIrql() != PASSIVE_LEVEL) { /* It didn't, bugcheck! */ KeBugCheckEx(WORKER_THREAD_RETURNED_AT_BAD_IRQL, (ULONG_PTR)WorkItem->WorkerRoutine, KeGetCurrentIrql(), (ULONG_PTR)WorkItem->Parameter, (ULONG_PTR)WorkItem); } /* Make sure it returned with Impersionation Disabled */ if (Thread->ActiveImpersonationInfo) { /* It didn't, bugcheck! */ KeBugCheckEx(IMPERSONATING_WORKER_THREAD, (ULONG_PTR)WorkItem->WorkerRoutine, (ULONG_PTR)WorkItem->Parameter, (ULONG_PTR)WorkItem, 0); } } /* This is a dynamic thread. Terminate it unless IRPs are pending */ if (!IsListEmpty(&Thread->IrpList)) goto ProcessLoop; /* Don't terminate it if the queue is disabled either */ if (WorkQueue->Info.QueueDisabled) goto ProcessLoop; /* Set the worker flags */ do { /* Decrease the worker count */ OldValue = WorkQueue->Info; NewValue = OldValue; NewValue.WorkerCount--; } while (InterlockedCompareExchange((PLONG)&WorkQueue->Info, *(PLONG)&NewValue, *(PLONG)&OldValue) != *(PLONG)&OldValue); /* Decrement dynamic thread count */ InterlockedDecrement(&WorkQueue->DynamicThreadCount); /* We're not a worker thread anymore */ Thread->ActiveExWorker = FALSE; /* Re-enable the stack swap */ KeSetKernelStackSwapEnable(TRUE); return; }
VOID ExpWorkerThread ( IN PVOID StartContext ) { PLIST_ENTRY Entry; WORK_QUEUE_TYPE QueueType; PWORK_QUEUE_ITEM WorkItem; KPROCESSOR_MODE WaitMode; LARGE_INTEGER TimeoutValue; PLARGE_INTEGER Timeout; PETHREAD Thread; PEX_WORK_QUEUE WorkerQueue; PWORKER_THREAD_ROUTINE WorkerRoutine; PVOID Parameter; EX_QUEUE_WORKER_INFO OldWorkerInfo; EX_QUEUE_WORKER_INFO NewWorkerInfo; ULONG CountForQueueEmpty; // // Set timeout value etc according to whether we are static or dynamic. // if (((ULONG_PTR)StartContext & DYNAMIC_WORKER_THREAD) == 0) { // // We are being created as a static thread. As such it will not // terminate, so there is no point in timing out waiting for a work // item. // Timeout = NULL; } else { // // This is a dynamic worker thread. It has a non-infinite timeout // so that it can eventually terminate. // TimeoutValue.QuadPart = -DYNAMIC_THREAD_TIMEOUT; Timeout = &TimeoutValue; } Thread = PsGetCurrentThread (); // // If the thread is a critical worker thread, then set the thread // priority to the lowest realtime level. Otherwise, set the base // thread priority to time critical. // QueueType = (WORK_QUEUE_TYPE) ((ULONG_PTR)StartContext & ~DYNAMIC_WORKER_THREAD); WorkerQueue = &ExWorkerQueue[QueueType]; WaitMode = (KPROCESSOR_MODE) WorkerQueue->Info.WaitMode; ASSERT (Thread->ExWorkerCanWaitUser == 0); if (WaitMode == UserMode) { Thread->ExWorkerCanWaitUser = 1; } #if defined(REMOTE_BOOT) // // In diskless NT scenarios ensure that the kernel stack of the worker // threads will not be swapped out. // if (IoRemoteBootClient) { KeSetKernelStackSwapEnable (FALSE); } #endif // defined(REMOTE_BOOT) // // Register as a worker, exiting if the queue's going down and // there aren't any workers in the queue to hand us the shutdown // work item if we enter the queue (we want to be able to enter a // queue even if the queue's shutting down, in case there's a // backlog of work items that the balance manager thread's decided // we should be helping to process). // if (PO_SHUTDOWN_QUEUE == QueueType) { CountForQueueEmpty = 1; } else { CountForQueueEmpty = 0; } if (ExpWorkersCanSwap == FALSE) { KeSetKernelStackSwapEnable (FALSE); } do { OldWorkerInfo.QueueWorkerInfo = ReadForWriteAccess (&WorkerQueue->Info.QueueWorkerInfo); if (OldWorkerInfo.QueueDisabled && OldWorkerInfo.WorkerCount <= CountForQueueEmpty) { // // The queue is disabled and empty so just exit. // KeSetKernelStackSwapEnable (TRUE); PsTerminateSystemThread (STATUS_SYSTEM_SHUTDOWN); } NewWorkerInfo.QueueWorkerInfo = OldWorkerInfo.QueueWorkerInfo; NewWorkerInfo.WorkerCount += 1; } while (OldWorkerInfo.QueueWorkerInfo != InterlockedCompareExchange (&WorkerQueue->Info.QueueWorkerInfo, NewWorkerInfo.QueueWorkerInfo, OldWorkerInfo.QueueWorkerInfo)); // // As of this point, we must only exit if we decrement the worker // count without the queue disabled flag being set. (Unless we // exit due to the shutdown work item, which also decrements the // worker count). // Thread->ActiveExWorker = 1; // // Loop forever waiting for a work queue item, calling the processing // routine, and then waiting for another work queue item. // do { // // Wait until something is put in the queue or until we time out. // // By specifying a wait mode of UserMode, the thread's kernel // stack is swappable. // Entry = KeRemoveQueue (&WorkerQueue->WorkerQueue, WaitMode, Timeout); if ((ULONG_PTR)Entry != STATUS_TIMEOUT) { // // This is a real work item, process it. // // Update the total number of work items processed. // InterlockedIncrement ((PLONG)&WorkerQueue->WorkItemsProcessed); WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List); WorkerRoutine = WorkItem->WorkerRoutine; Parameter = WorkItem->Parameter; // // Catch worker routines referencing a user mode address. // ASSERT ((ULONG_PTR)WorkerRoutine > MmUserProbeAddress); // // Execute the specified routine. // ((PWORKER_THREAD_ROUTINE)WorkerRoutine) (Parameter); #if DBG if (IsListEmpty (&Thread->IrpList)) { // // See if a worker just returned while holding a resource // ExCheckIfResourceOwned (); } #endif // // Catch worker routines that forget to leave a critial/guarded // region. In the debug case execute a breakpoint. In the free // case zero the flag so that APCs can continue to fire to this // thread. // if (Thread->Tcb.CombinedApcDisable != 0) { DbgPrint ((char*)ExpWorkerApcDisabledMessage, WorkerRoutine, Parameter, WorkItem); ASSERT (FALSE); Thread->Tcb.CombinedApcDisable = 0; } if (KeGetCurrentIrql () != PASSIVE_LEVEL) { KeBugCheckEx (WORKER_THREAD_RETURNED_AT_BAD_IRQL, (ULONG_PTR)WorkerRoutine, (ULONG_PTR)KeGetCurrentIrql(), (ULONG_PTR)Parameter, (ULONG_PTR)WorkItem); } if (PS_IS_THREAD_IMPERSONATING (Thread)) { KeBugCheckEx (IMPERSONATING_WORKER_THREAD, (ULONG_PTR)WorkerRoutine, (ULONG_PTR)Parameter, (ULONG_PTR)WorkItem, 0); } continue; } // // These things are known: // // - Static worker threads do not time out, so this is a dynamic // worker thread. // // - This thread has been waiting for a long time with nothing // to do. // if (IsListEmpty (&Thread->IrpList) == FALSE) { // // There is still I/O pending, can't terminate yet. // continue; } // // Get out of the queue, if we can // do { OldWorkerInfo.QueueWorkerInfo = ReadForWriteAccess (&WorkerQueue->Info.QueueWorkerInfo); if (OldWorkerInfo.QueueDisabled) { // // We're exiting via the queue disable work item; // there's no point in expiring here. // break; } NewWorkerInfo.QueueWorkerInfo = OldWorkerInfo.QueueWorkerInfo; NewWorkerInfo.WorkerCount -= 1; } while (OldWorkerInfo.QueueWorkerInfo != InterlockedCompareExchange(&WorkerQueue->Info.QueueWorkerInfo, NewWorkerInfo.QueueWorkerInfo, OldWorkerInfo.QueueWorkerInfo)); if (OldWorkerInfo.QueueDisabled) { // // We're exiting via the queue disable work item // continue; } // // This dynamic thread can be terminated. // break; } while (TRUE); // // Terminate this dynamic thread. // InterlockedDecrement ((PLONG)&WorkerQueue->DynamicThreadCount); // // Carefully clear this before marking the thread stack as swap enabled // so that an incoming APC won't inadvertently disable the stack swap // afterwards. // Thread->ActiveExWorker = 0; // // We will bugcheck if we terminate a thread with stack swapping // disabled. // KeSetKernelStackSwapEnable (TRUE); return; }
/* * @implemented */ VOID NTAPI FsRtlWorkerThread(IN PVOID StartContext) { KIRQL Irql; PLIST_ENTRY Entry; PWORK_QUEUE_ITEM WorkItem; ULONG QueueId = (ULONG)StartContext; /* Set our priority according to the queue we're dealing with */ KeSetPriorityThread(&PsGetCurrentThread()->Tcb, LOW_REALTIME_PRIORITY + QueueId); /* Loop for events */ for (;;) { /* Look for next event */ Entry = KeRemoveQueue(&FsRtlWorkerQueues[QueueId], KernelMode, NULL); WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List); /* Call its routine (here: FsRtlStackOverflowRead) */ WorkItem->WorkerRoutine(WorkItem->Parameter); /* Check we're still at passive level or bugcheck */ Irql = KeGetCurrentIrql(); if (Irql != PASSIVE_LEVEL) { KeBugCheckEx(IRQL_NOT_LESS_OR_EQUAL, (ULONG_PTR)WorkItem->WorkerRoutine, (ULONG_PTR)Irql, (ULONG_PTR)WorkItem->WorkerRoutine, (ULONG_PTR)WorkItem); } } }