/** * Frees resources used by a work queue. * * \param WorkQueue A work queue object. */ VOID PhDeleteWorkQueue( __inout PPH_WORK_QUEUE WorkQueue ) { PLIST_ENTRY listEntry; PPH_WORK_QUEUE_ITEM workQueueItem; #ifdef DEBUG PhAcquireQueuedLockExclusive(&PhDbgWorkQueueListLock); RemoveEntryList(&WorkQueue->DbgListEntry); PhReleaseQueuedLockExclusive(&PhDbgWorkQueueListLock); #endif // Wait for all worker threads to exit. WorkQueue->Terminating = TRUE; NtReleaseSemaphore(WorkQueue->SemaphoreHandle, WorkQueue->CurrentThreads, NULL); PhWaitForRundownProtection(&WorkQueue->RundownProtect); // Free all un-executed work items. listEntry = WorkQueue->QueueListHead.Flink; while (listEntry != &WorkQueue->QueueListHead) { workQueueItem = CONTAINING_RECORD(listEntry, PH_WORK_QUEUE_ITEM, ListEntry); listEntry = listEntry->Flink; PhFreeToFreeList(&PhWorkQueueItemFreeList, workQueueItem); } NtClose(WorkQueue->SemaphoreHandle); }
FORCEINLINE VOID PhpDestroyWorkQueueItem( _In_ PPH_WORK_QUEUE_ITEM WorkQueueItem ) { if (WorkQueueItem->DeleteFunction) WorkQueueItem->DeleteFunction(WorkQueueItem->Function, WorkQueueItem->Context); PhFreeToFreeList(&PhWorkQueueItemFreeList, WorkQueueItem); }
/** * Calls the delete procedure for an object and frees its * allocated storage. * * \param ObjectHeader A pointer to the object header of an allocated object. */ VOID PhpFreeObject( __in PPH_OBJECT_HEADER ObjectHeader ) { /* Object type statistics. */ _InterlockedDecrement(&ObjectHeader->Type->NumberOfObjects); #ifdef DEBUG PhAcquireQueuedLockExclusive(&PhDbgObjectListLock); RemoveEntryList(&ObjectHeader->ObjectListEntry); PhReleaseQueuedLockExclusive(&PhDbgObjectListLock); #endif REF_STAT_UP(RefObjectsDestroyed); /* Call the delete procedure if we have one. */ if (ObjectHeader->Type->DeleteProcedure) { ObjectHeader->Type->DeleteProcedure( PhObjectHeaderToObject(ObjectHeader), 0 ); } if (ObjectHeader->Flags & PHOBJ_FROM_TYPE_FREE_LIST) { PhFreeToFreeList(&ObjectHeader->Type->FreeList, ObjectHeader); REF_STAT_UP(RefObjectsFreedToTypeFreeList); } else if (ObjectHeader->Flags & PHOBJ_FROM_SMALL_FREE_LIST) { PhFreeToFreeList(&PhObjectSmallFreeList, ObjectHeader); REF_STAT_UP(RefObjectsFreedToSmallFreeList); } else { PhFree(ObjectHeader); REF_STAT_UP(RefObjectsFreed); } }
static VOID NTAPI ProcessesUpdatedCallback( __in_opt PVOID Parameter, __in_opt PVOID Context ) { static ULONG runCount = 0; PSLIST_ENTRY listEntry; PLIST_ENTRY ageListEntry; // Process incoming disk event packets. listEntry = RtlInterlockedFlushSList(&EtDiskPacketListHead); while (listEntry) { PETP_DISK_PACKET packet; packet = CONTAINING_RECORD(listEntry, ETP_DISK_PACKET, ListEntry); listEntry = listEntry->Next; EtpProcessDiskPacket(packet, runCount); if (packet->FileName) PhDereferenceObject(packet->FileName); PhFreeToFreeList(&EtDiskPacketFreeList, packet); } // Remove old entries. ageListEntry = EtDiskAgeListHead.Blink; while (ageListEntry != &EtDiskAgeListHead) { PET_DISK_ITEM diskItem; diskItem = CONTAINING_RECORD(ageListEntry, ET_DISK_ITEM, AgeListEntry); ageListEntry = ageListEntry->Blink; if (runCount - diskItem->FreshTime < HISTORY_SIZE) // must compare like this to avoid overflow/underflow problems break; PhInvokeCallback(&EtDiskItemRemovedEvent, diskItem); PhAcquireQueuedLockExclusive(&EtDiskHashtableLock); EtpRemoveDiskItem(diskItem); PhReleaseQueuedLockExclusive(&EtDiskHashtableLock); } // Update existing items. ageListEntry = EtDiskAgeListHead.Flink; while (ageListEntry != &EtDiskAgeListHead) { PET_DISK_ITEM diskItem; diskItem = CONTAINING_RECORD(ageListEntry, ET_DISK_ITEM, AgeListEntry); // Update statistics. if (diskItem->HistoryPosition != 0) diskItem->HistoryPosition--; else diskItem->HistoryPosition = HISTORY_SIZE - 1; diskItem->ReadHistory[diskItem->HistoryPosition] = diskItem->ReadDelta; diskItem->WriteHistory[diskItem->HistoryPosition] = diskItem->WriteDelta; if (diskItem->HistoryCount < HISTORY_SIZE) diskItem->HistoryCount++; if (diskItem->ResponseTimeCount != 0) { diskItem->ResponseTimeAverage = (FLOAT)diskItem->ResponseTimeTotal / diskItem->ResponseTimeCount; // Reset the total once in a while to avoid the number getting too large (and thus losing precision). if (diskItem->ResponseTimeCount >= 1000) { diskItem->ResponseTimeTotal = diskItem->ResponseTimeAverage; diskItem->ResponseTimeCount = 1; } } diskItem->ReadTotal += diskItem->ReadDelta; diskItem->WriteTotal += diskItem->WriteDelta; diskItem->ReadDelta = 0; diskItem->WriteDelta = 0; diskItem->ReadAverage = EtpCalculateAverage(diskItem->ReadHistory, HISTORY_SIZE, diskItem->HistoryPosition, diskItem->HistoryCount, HISTORY_SIZE); diskItem->WriteAverage = EtpCalculateAverage(diskItem->WriteHistory, HISTORY_SIZE, diskItem->HistoryPosition, diskItem->HistoryCount, HISTORY_SIZE); if (diskItem->AddTime != runCount) { BOOLEAN modified = FALSE; PPH_PROCESS_ITEM processItem; if (!diskItem->ProcessName || !diskItem->ProcessIcon || !diskItem->ProcessRecord) { if (processItem = PhReferenceProcessItem(diskItem->ProcessId)) { if (!diskItem->ProcessName) { diskItem->ProcessName = processItem->ProcessName; PhReferenceObject(processItem->ProcessName); modified = TRUE; } if (!diskItem->ProcessIcon) { diskItem->ProcessIcon = EtProcIconReferenceSmallProcessIcon(EtGetProcessBlock(processItem)); if (diskItem->ProcessIcon) modified = TRUE; } if (!diskItem->ProcessRecord) { diskItem->ProcessRecord = processItem->Record; PhReferenceProcessRecord(diskItem->ProcessRecord); } PhDereferenceObject(processItem); } } if (modified) { // Raise the disk item modified event. PhInvokeCallback(&EtDiskItemModifiedEvent, diskItem); } } ageListEntry = ageListEntry->Flink; } PhInvokeCallback(&EtDiskItemsUpdatedEvent, NULL); runCount++; }
NTSTATUS PhpWorkQueueThreadStart( __in PVOID Parameter ) { PPH_WORK_QUEUE workQueue = (PPH_WORK_QUEUE)Parameter; while (TRUE) { NTSTATUS status; LARGE_INTEGER timeout; PPH_WORK_QUEUE_ITEM workQueueItem = NULL; // Check if we have more threads than the limit. if (workQueue->CurrentThreads > workQueue->MaximumThreads) { BOOLEAN terminate = FALSE; // Lock and re-check. PhAcquireQueuedLockExclusive(&workQueue->StateLock); // Check the minimum as well. if ( workQueue->CurrentThreads > workQueue->MaximumThreads && workQueue->CurrentThreads > workQueue->MinimumThreads ) { workQueue->CurrentThreads--; terminate = TRUE; } PhReleaseQueuedLockExclusive(&workQueue->StateLock); if (terminate) break; } // Wait for work. status = NtWaitForSingleObject( workQueue->SemaphoreHandle, FALSE, PhTimeoutFromMilliseconds(&timeout, workQueue->NoWorkTimeout) ); if (workQueue->Terminating) { // The work queue is being deleted. PhAcquireQueuedLockExclusive(&workQueue->StateLock); workQueue->CurrentThreads--; PhReleaseQueuedLockExclusive(&workQueue->StateLock); break; } if (status == STATUS_WAIT_0) { PLIST_ENTRY listEntry; // Dequeue the work item. PhAcquireQueuedLockExclusive(&workQueue->QueueLock); listEntry = RemoveHeadList(&workQueue->QueueListHead); PhReleaseQueuedLockExclusive(&workQueue->QueueLock); // Make sure we got work. if (listEntry != &workQueue->QueueListHead) { workQueueItem = CONTAINING_RECORD(listEntry, PH_WORK_QUEUE_ITEM, ListEntry); _InterlockedIncrement(&workQueue->BusyThreads); PhpExecuteWorkQueueItem(workQueueItem); _InterlockedDecrement(&workQueue->BusyThreads); PhFreeToFreeList(&PhWorkQueueItemFreeList, workQueueItem); } } else { BOOLEAN terminate = FALSE; // No work arrived before the timeout passed (or some error occurred). // Terminate the thread. PhAcquireQueuedLockExclusive(&workQueue->StateLock); // Check the minimum. if (workQueue->CurrentThreads > workQueue->MinimumThreads) { workQueue->CurrentThreads--; terminate = TRUE; } PhReleaseQueuedLockExclusive(&workQueue->StateLock); if (terminate) break; } } PhReleaseRundownProtection(&workQueue->RundownProtect); return STATUS_SUCCESS; }