BOOLEAN PhpCreateWorkQueueThread( _Inout_ PPH_WORK_QUEUE WorkQueue ) { HANDLE threadHandle; // Make sure the structure doesn't get deleted while the thread is running. if (!PhAcquireRundownProtection(&WorkQueue->RundownProtect)) return FALSE; threadHandle = PhCreateThread(0, PhpWorkQueueThreadStart, WorkQueue); if (threadHandle) { PHLIB_INC_STATISTIC(WqWorkQueueThreadsCreated); WorkQueue->CurrentThreads++; NtClose(threadHandle); return TRUE; } else { PHLIB_INC_STATISTIC(WqWorkQueueThreadsCreateFailed); PhReleaseRundownProtection(&WorkQueue->RundownProtect); return FALSE; } }
/** * Queues a work item to a work queue. * * \param WorkQueue A work queue object. * \param Function A function to execute. * \param Context A user-defined value to pass to the function. * \param DeleteFunction A callback function that is executed when the work queue item is about to be freed. */ VOID PhQueueItemWorkQueueEx( _Inout_ PPH_WORK_QUEUE WorkQueue, _In_ PUSER_THREAD_START_ROUTINE Function, _In_opt_ PVOID Context, _In_opt_ PPH_WORK_QUEUE_ITEM_DELETE_FUNCTION DeleteFunction ) { PPH_WORK_QUEUE_ITEM workQueueItem; workQueueItem = PhpCreateWorkQueueItem(Function, Context, DeleteFunction); // Enqueue the work item. PhAcquireQueuedLockExclusive(&WorkQueue->QueueLock); InsertTailList(&WorkQueue->QueueListHead, &workQueueItem->ListEntry); _InterlockedIncrement(&WorkQueue->BusyCount); PhReleaseQueuedLockExclusive(&WorkQueue->QueueLock); // Signal the semaphore once to let a worker thread continue. NtReleaseSemaphore(PhpGetSemaphoreWorkQueue(WorkQueue), 1, NULL); PHLIB_INC_STATISTIC(WqWorkItemsQueued); // Check if all worker threads are currently busy, and if we can create more threads. if (WorkQueue->BusyCount >= WorkQueue->CurrentThreads && WorkQueue->CurrentThreads < WorkQueue->MaximumThreads) { // Lock and re-check. PhAcquireQueuedLockExclusive(&WorkQueue->StateLock); if (WorkQueue->CurrentThreads < WorkQueue->MaximumThreads) PhpCreateWorkQueueThread(WorkQueue); PhReleaseQueuedLockExclusive(&WorkQueue->StateLock); } }
/** * Waits for a wait block to be unblocked. * * \param WaitBlock A wait block. * \param Spin TRUE to spin, FALSE to block immediately. * \param Timeout A timeout value. */ __mayRaise FORCEINLINE NTSTATUS PhpBlockOnQueuedWaitBlock( __inout PPH_QUEUED_WAIT_BLOCK WaitBlock, __in BOOLEAN Spin, __in_opt PLARGE_INTEGER Timeout ) { NTSTATUS status; ULONG i; if (Spin) { PHLIB_INC_STATISTIC(QlBlockSpins); for (i = PhQueuedLockSpinCount; i != 0; i--) { if (!(*(volatile ULONG *)&WaitBlock->Flags & PH_QUEUED_WAITER_SPINNING)) return STATUS_SUCCESS; YieldProcessor(); } } if (_interlockedbittestandreset((PLONG)&WaitBlock->Flags, PH_QUEUED_WAITER_SPINNING_SHIFT)) { PHLIB_INC_STATISTIC(QlBlockWaits); status = NtWaitForKeyedEvent( PhQueuedLockKeyedEventHandle, WaitBlock, FALSE, Timeout ); // If an error occurred (timeout is not an error), raise an exception // as it is nearly impossible to recover from this situation. if (!NT_SUCCESS(status)) PhRaiseStatus(status); } else { status = STATUS_SUCCESS; } return status; }
/** * Acquires a queued lock in shared mode. * * \param QueuedLock A queued lock. */ VOID FASTCALL PhfAcquireQueuedLockShared( __inout PPH_QUEUED_LOCK QueuedLock ) { ULONG_PTR value; ULONG_PTR newValue; ULONG_PTR currentValue; BOOLEAN optimize; PH_QUEUED_WAIT_BLOCK waitBlock; value = QueuedLock->Value; while (TRUE) { // We can't acquire if there are waiters for two reasons: // // We want to prioritize exclusive acquires over shared acquires. // There's currently no fast, safe way of finding the last wait // block and incrementing the shared owners count here. if ( !(value & PH_QUEUED_LOCK_WAITERS) && (!(value & PH_QUEUED_LOCK_OWNED) || (PhGetQueuedLockSharedOwners(value) > 0)) ) { newValue = (value + PH_QUEUED_LOCK_SHARED_INC) | PH_QUEUED_LOCK_OWNED; if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( (PPVOID)&QueuedLock->Value, (PVOID)newValue, (PVOID)value )) == value) break; } else { if (PhpPushQueuedWaitBlock( QueuedLock, value, FALSE, &waitBlock, &optimize, &newValue, ¤tValue )) { if (optimize) PhpfOptimizeQueuedLockList(QueuedLock, currentValue); PHLIB_INC_STATISTIC(QlAcquireSharedBlocks); PhpBlockOnQueuedWaitBlock(&waitBlock, TRUE, NULL); } } value = newValue; } }
/** * Acquires a queued lock in exclusive mode. * * \param QueuedLock A queued lock. */ VOID FASTCALL PhfAcquireQueuedLockExclusive( __inout PPH_QUEUED_LOCK QueuedLock ) { ULONG_PTR value; ULONG_PTR newValue; ULONG_PTR currentValue; BOOLEAN optimize; PH_QUEUED_WAIT_BLOCK waitBlock; value = QueuedLock->Value; while (TRUE) { if (!(value & PH_QUEUED_LOCK_OWNED)) { if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( (PPVOID)&QueuedLock->Value, (PVOID)(value + PH_QUEUED_LOCK_OWNED), (PVOID)value )) == value) break; } else { if (PhpPushQueuedWaitBlock( QueuedLock, value, TRUE, &waitBlock, &optimize, &newValue, ¤tValue )) { if (optimize) PhpfOptimizeQueuedLockList(QueuedLock, currentValue); PHLIB_INC_STATISTIC(QlAcquireExclusiveBlocks); PhpBlockOnQueuedWaitBlock(&waitBlock, TRUE, NULL); } } value = newValue; } }
/** * Queues a work item to a work queue. * * \param WorkQueue A work queue object. * \param Function A function to execute. * \param Context A user-defined value to pass to the function. */ VOID PhQueueItemWorkQueue( __inout PPH_WORK_QUEUE WorkQueue, __in PTHREAD_START_ROUTINE Function, __in_opt PVOID Context ) { PPH_WORK_QUEUE_ITEM workQueueItem; workQueueItem = PhAllocateFromFreeList(&PhWorkQueueItemFreeList); PhpInitializeWorkQueueItem(workQueueItem, Function, Context); // Enqueue the work item. PhAcquireQueuedLockExclusive(&WorkQueue->QueueLock); InsertTailList(&WorkQueue->QueueListHead, &workQueueItem->ListEntry); PhReleaseQueuedLockExclusive(&WorkQueue->QueueLock); // Signal the semaphore once to let a worker thread continue. NtReleaseSemaphore(WorkQueue->SemaphoreHandle, 1, NULL); PHLIB_INC_STATISTIC(WqWorkItemsQueued); // Check if all worker threads are currently busy, // and if we can create more threads. if ( WorkQueue->BusyThreads == WorkQueue->CurrentThreads && WorkQueue->CurrentThreads < WorkQueue->MaximumThreads ) { // Lock and re-check. PhAcquireQueuedLockExclusive(&WorkQueue->StateLock); if (WorkQueue->CurrentThreads < WorkQueue->MaximumThreads) { PhpCreateWorkQueueThread(WorkQueue); } PhReleaseQueuedLockExclusive(&WorkQueue->StateLock); } }