static NTSTATUS WorkLoop( PLW_WORK_THREAD pThread ) { NTSTATUS status = STATUS_SUCCESS; PRING pRing = NULL; PLW_WORK_ITEM pItem = NULL; PLW_WORK_THREADS pThreads = pThread->pThreads; LOCK_THREADS(pThread->pThreads); for(;;) { pThreads->ulAvailable++; status = WorkWait(pThread); GOTO_ERROR_ON_STATUS(status); RingDequeue(&pThreads->WorkItems, &pRing); pThreads->ulQueued--; pThreads->ulAvailable--; UNLOCK_THREADS(pThreads); pItem = LW_STRUCT_FROM_FIELD(pRing, LW_WORK_ITEM, Ring); pItem->pfnFunc(pItem, pItem->pContext); LOCK_THREADS(pThreads); } error: pThreads->ulAvailable--; pThreads->ulStarted--; pThread->bStarted = FALSE; /* If the thread pool is not being shut down, nothing is going to call pthread_join() on this thread, so call pthread_detach() now */ if (!pThreads->bShutdown) { pthread_detach(pThread->Thread); pThread->Thread = INVALID_THREAD_HANDLE; } UNLOCK_THREADS(pThreads); return status; }
VOID DestroyWorkThreads( PLW_WORK_THREADS pThreads ) { size_t i = 0; if (pThreads->pWorkThreads) { WaitWorkItems(pThreads); LOCK_THREADS(pThreads); pThreads->bShutdown = TRUE; pthread_cond_broadcast(&pThreads->Event); for (i = 0; i < pThreads->ulWorkThreadCount; i++) { if (pThreads->pWorkThreads[i].Thread != INVALID_THREAD_HANDLE) { /* We must pthread_join() outside of the lock */ UNLOCK_THREADS(pThreads); pthread_join(pThreads->pWorkThreads[i].Thread, NULL); LOCK_THREADS(pThreads); } } UNLOCK_THREADS(pThreads); RtlMemoryFree(pThreads->pWorkThreads); } if (pThreads->bDestroyLock) { pthread_mutex_destroy(&pThreads->Lock); } if (pThreads->bDestroyEvent) { pthread_cond_destroy(&pThreads->Event); } }
VOID WaitWorkItems( PLW_WORK_THREADS pThreads ) { LOCK_THREADS(pThreads); pThreads->bWaiting = TRUE; while (pThreads->ulWorkItemCount) { pthread_cond_wait(&pThreads->Event, &pThreads->Lock); } pThreads->bWaiting = FALSE; UNLOCK_THREADS(pThreads); }
VOID FreeWorkItem( LW_IN LW_OUT PLW_WORK_ITEM* ppWorkItem ) { if (*ppWorkItem) { LOCK_THREADS((*ppWorkItem)->pThreads); (*ppWorkItem)->pThreads->ulWorkItemCount--; if ((*ppWorkItem)->pThreads->bWaiting || ((*ppWorkItem)->pThreads->ulWorkItemCount == 0 && (*ppWorkItem)->pThreads->ulStarted == 0)) { pthread_cond_broadcast(&(*ppWorkItem)->pThreads->Event); } UNLOCK_THREADS((*ppWorkItem)->pThreads); } RTL_FREE(ppWorkItem); }
NTSTATUS CreateWorkItem( LW_IN PLW_WORK_THREADS pThreads, LW_OUT PLW_WORK_ITEM* ppWorkItem, LW_WORK_ITEM_FUNCTION pfnFunc, PVOID pContext ) { PLW_WORK_ITEM pItem = NULL; NTSTATUS status = STATUS_SUCCESS; LOCK_THREADS(pThreads); if (pThreads->ulStarted == 0) { /* Make sure at least one thread is running */ status = StartWorkThread(pThreads, &pThreads->pWorkThreads[0]); GOTO_ERROR_ON_STATUS(status); } status = LW_RTL_ALLOCATE_AUTO(&pItem); GOTO_ERROR_ON_STATUS(status); RingInit(&pItem->Ring); pItem->pThreads = pThreads; pItem->pfnFunc = pfnFunc; pItem->pContext = pContext; pThreads->ulWorkItemCount++; error: UNLOCK_THREADS(pThreads); *ppWorkItem = pItem; return status; }
VOID ScheduleWorkItem( PLW_WORK_THREADS pThreads, PLW_WORK_ITEM pItem, LW_SCHEDULE_FLAGS Flags ) { size_t i = 0; if (pThreads == NULL) { pThreads = pItem->pThreads; } LOCK_THREADS(pThreads); assert(pThreads->ulStarted > 0); /* Enqueue work item */ if (Flags & LW_SCHEDULE_HIGH_PRIORITY) { RingEnqueueFront(&pThreads->WorkItems, &pItem->Ring); } else { RingEnqueue(&pThreads->WorkItems, &pItem->Ring); } pThreads->ulQueued++; /* * If there are more pending work items than there * are available threads, and not all threads are started, * try to start another one to handle the additional load */ if (pThreads->ulAvailable < pThreads->ulQueued && pThreads->ulStarted < pThreads->ulWorkThreadCount) { for (i = 0; i < pThreads->ulWorkThreadCount; i++) { if (!pThreads->pWorkThreads[i].bStarted) { if (StartWorkThread(pThreads, &pThreads->pWorkThreads[i]) != STATUS_SUCCESS) { LW_RTL_LOG_WARNING("Could not start work item thread"); /* Signal an existing thread instead */ pthread_cond_signal(&pThreads->Event); } break; } } } else if (pThreads->ulAvailable) { /* Signal an existing thread */ pthread_cond_signal(&pThreads->Event); } UNLOCK_THREADS(pThreads); }