int ThreadPoolRemove(ThreadPool *tp, int jobId, ThreadPoolJob *out) { int ret = INVALID_JOB_ID; ThreadPoolJob *temp = NULL; ListNode *tempNode = NULL; ThreadPoolJob dummy; if (!tp) return EINVAL; if (!out) out = &dummy; dummy.jobId = jobId; ithread_mutex_lock(&tp->mutex); tempNode = ListFind(&tp->highJobQ, NULL, &dummy); if (tempNode) { temp = (ThreadPoolJob *)tempNode->item; *out = *temp; ListDelNode(&tp->highJobQ, tempNode, 0); FreeThreadPoolJob(tp, temp); ret = 0; goto exit_function; } tempNode = ListFind(&tp->medJobQ, NULL, &dummy); if (tempNode) { temp = (ThreadPoolJob *)tempNode->item; *out = *temp; ListDelNode(&tp->medJobQ, tempNode, 0); FreeThreadPoolJob(tp, temp); ret = 0; goto exit_function; } tempNode = ListFind(&tp->lowJobQ, NULL, &dummy); if (tempNode) { temp = (ThreadPoolJob *)tempNode->item; *out = *temp; ListDelNode(&tp->lowJobQ, tempNode, 0); FreeThreadPoolJob(tp, temp); ret = 0; goto exit_function; } if (tp->persistentJob && tp->persistentJob->jobId == jobId) { *out = *tp->persistentJob; FreeThreadPoolJob(tp, tp->persistentJob); tp->persistentJob = NULL; ret = 0; goto exit_function; } exit_function: ithread_mutex_unlock(&tp->mutex); return ret; }
int ThreadPoolAdd(ThreadPool *tp, ThreadPoolJob *job, int *jobId) { int rc = EOUTOFMEM; int tempId = -1; long totalJobs; ThreadPoolJob *temp = NULL; if (!tp || !job) return EINVAL; ithread_mutex_lock(&tp->mutex); totalJobs = tp->highJobQ.size + tp->lowJobQ.size + tp->medJobQ.size; if (totalJobs >= tp->attr.maxJobsTotal) { fprintf(stderr, "total jobs = %ld, too many jobs", totalJobs); goto exit_function; } if (!jobId) jobId = &tempId; *jobId = INVALID_JOB_ID; temp = CreateThreadPoolJob(job, tp->lastJobId, tp); if (!temp) goto exit_function; switch (job->priority) { case HIGH_PRIORITY: if (ListAddTail(&tp->highJobQ, temp)) rc = 0; break; case MED_PRIORITY: if (ListAddTail(&tp->medJobQ, temp)) rc = 0; break; default: if (ListAddTail(&tp->lowJobQ, temp)) rc = 0; } /* AddWorker if appropriate */ AddWorker(tp); /* Notify a waiting thread */ if (rc == 0) ithread_cond_signal(&tp->condition); else FreeThreadPoolJob(tp, temp); *jobId = tp->lastJobId++; exit_function: ithread_mutex_unlock(&tp->mutex); return rc; }
int ThreadPoolShutdown(ThreadPool *tp) { ListNode *head = NULL; ThreadPoolJob *temp = NULL; if (!tp) return EINVAL; ithread_mutex_lock(&tp->mutex); /* clean up high priority jobs */ while (tp->highJobQ.size) { head = ListHead(&tp->highJobQ); if (head == NULL) { ithread_mutex_unlock(&tp->mutex); return EINVAL; } temp = (ThreadPoolJob *)head->item; if (temp->free_func) temp->free_func(temp->arg); FreeThreadPoolJob(tp, temp); ListDelNode(&tp->highJobQ, head, 0); } ListDestroy(&tp->highJobQ, 0); /* clean up med priority jobs */ while (tp->medJobQ.size) { head = ListHead(&tp->medJobQ); if (head == NULL) { ithread_mutex_unlock(&tp->mutex); return EINVAL; } temp = (ThreadPoolJob *)head->item; if (temp->free_func) temp->free_func(temp->arg); FreeThreadPoolJob(tp, temp); ListDelNode(&tp->medJobQ, head, 0); } ListDestroy(&tp->medJobQ, 0); /* clean up low priority jobs */ while (tp->lowJobQ.size) { head = ListHead(&tp->lowJobQ); if (head == NULL) { ithread_mutex_unlock(&tp->mutex); return EINVAL; } temp = (ThreadPoolJob *)head->item; if (temp->free_func) temp->free_func(temp->arg); FreeThreadPoolJob(tp, temp); ListDelNode(&tp->lowJobQ, head, 0); } ListDestroy(&tp->lowJobQ, 0); /* clean up long term job */ if (tp->persistentJob) { temp = tp->persistentJob; if (temp->free_func) temp->free_func(temp->arg); FreeThreadPoolJob(tp, temp); tp->persistentJob = NULL; } /* signal shutdown */ tp->shutdown = 1; ithread_cond_broadcast(&tp->condition); /* wait for all threads to finish */ while (tp->totalThreads > 0) ithread_cond_wait(&tp->start_and_shutdown, &tp->mutex); /* destroy condition */ while (ithread_cond_destroy(&tp->condition) != 0) {} while (ithread_cond_destroy(&tp->start_and_shutdown) != 0) {} FreeListDestroy(&tp->jobFreeList); ithread_mutex_unlock(&tp->mutex); /* destroy mutex */ while (ithread_mutex_destroy(&tp->mutex) != 0) {} return 0; }
/*! * \brief Implements a thread pool worker. Worker waits for a job to become * available. Worker picks up persistent jobs first, high priority, * med priority, then low priority. * * If worker remains idle for more than specified max, the worker is released. * * \internal */ static void *WorkerThread( /*! arg -> is cast to (ThreadPool *). */ void *arg) { time_t start = 0; ThreadPoolJob *job = NULL; ListNode *head = NULL; struct timespec timeout; int retCode = 0; int persistent = -1; ThreadPool *tp = (ThreadPool *) arg; ithread_initialize_thread(); /* Increment total thread count */ ithread_mutex_lock(&tp->mutex); tp->totalThreads++; tp->pendingWorkerThreadStart = 0; ithread_cond_broadcast(&tp->start_and_shutdown); ithread_mutex_unlock(&tp->mutex); SetSeed(); StatsTime(&start); while (1) { ithread_mutex_lock(&tp->mutex); if (job) { tp->busyThreads--; FreeThreadPoolJob(tp, job); job = NULL; } retCode = 0; tp->stats.idleThreads++; tp->stats.totalWorkTime += (double)StatsTime(NULL) - (double)start; StatsTime(&start); if (persistent == 0) { tp->stats.workerThreads--; } else if (persistent == 1) { /* Persistent thread becomes a regular thread */ tp->persistentThreads--; } /* Check for a job or shutdown */ while (tp->lowJobQ.size == 0 && tp->medJobQ.size == 0 && tp->highJobQ.size == 0 && !tp->persistentJob && !tp->shutdown) { /* If wait timed out and we currently have more than the * min threads, or if we have more than the max threads * (only possible if the attributes have been reset) * let this thread die. */ if ((retCode == ETIMEDOUT && tp->totalThreads > tp->attr.minThreads) || (tp->attr.maxThreads != -1 && tp->totalThreads > tp->attr.maxThreads)) { tp->stats.idleThreads--; goto exit_function; } SetRelTimeout(&timeout, tp->attr.maxIdleTime); /* wait for a job up to the specified max time */ retCode = ithread_cond_timedwait( &tp->condition, &tp->mutex, &timeout); } tp->stats.idleThreads--; /* idle time */ tp->stats.totalIdleTime += (double)StatsTime(NULL) - (double)start; /* work time */ StatsTime(&start); /* bump priority of starved jobs */ BumpPriority(tp); /* if shutdown then stop */ if (tp->shutdown) { goto exit_function; } else { /* Pick up persistent job if available */ if (tp->persistentJob) { job = tp->persistentJob; tp->persistentJob = NULL; tp->persistentThreads++; persistent = 1; ithread_cond_broadcast(&tp->start_and_shutdown); } else { tp->stats.workerThreads++; persistent = 0; /* Pick the highest priority job */ if (tp->highJobQ.size > 0) { head = ListHead(&tp->highJobQ); if (head == NULL) { tp->stats.workerThreads--; goto exit_function; } job = (ThreadPoolJob *) head->item; CalcWaitTime(tp, HIGH_PRIORITY, job); ListDelNode(&tp->highJobQ, head, 0); } else if (tp->medJobQ.size > 0) { head = ListHead(&tp->medJobQ); if (head == NULL) { tp->stats.workerThreads--; goto exit_function; } job = (ThreadPoolJob *) head->item; CalcWaitTime(tp, MED_PRIORITY, job); ListDelNode(&tp->medJobQ, head, 0); } else if (tp->lowJobQ.size > 0) { head = ListHead(&tp->lowJobQ); if (head == NULL) { tp->stats.workerThreads--; goto exit_function; } job = (ThreadPoolJob *) head->item; CalcWaitTime(tp, LOW_PRIORITY, job); ListDelNode(&tp->lowJobQ, head, 0); } else { /* Should never get here */ tp->stats.workerThreads--; goto exit_function; } } } tp->busyThreads++; ithread_mutex_unlock(&tp->mutex); /* In the future can log info */ if (SetPriority(job->priority) != 0) { } else { } /* run the job */ job->func(job->arg); /* return to Normal */ SetPriority(DEFAULT_PRIORITY); } exit_function: tp->totalThreads--; ithread_cond_broadcast(&tp->start_and_shutdown); ithread_mutex_unlock(&tp->mutex); ithread_cleanup_thread(); return NULL; }
/**************************************************************************** * Function: ThreadPoolAdd * * Description: * Adds a job to the thread pool. * Job will be run as soon as possible. * Parameters: * tp - valid thread pool pointer * func - ThreadFunction to run * arg - argument to function. * priority - priority of job. * jobId - id of job * duration - whether or not this is a persistent thread * free_function - function to use when freeing argument * Returns: * 0 on success, nonzero on failure * EOUTOFMEM if not enough memory to add job. *****************************************************************************/ int ThreadPoolAdd( ThreadPool *tp, ThreadPoolJob *job, int *jobId ) { int rc = EOUTOFMEM; int tempId = -1; int totalJobs; ThreadPoolJob *temp = NULL; assert( tp != NULL ); assert( job != NULL ); if( ( tp == NULL ) || ( job == NULL ) ) { return EINVAL; } ithread_mutex_lock( &tp->mutex ); assert( job->priority == LOW_PRIORITY || job->priority == MED_PRIORITY || job->priority == HIGH_PRIORITY ); totalJobs = tp->highJobQ.size + tp->lowJobQ.size + tp->medJobQ.size; if (totalJobs >= tp->attr.maxJobsTotal) { fprintf(stderr, "total jobs = %d, too many jobs", totalJobs); ithread_mutex_unlock( &tp->mutex ); return rc; } if( jobId == NULL ) { jobId = &tempId; } *jobId = INVALID_JOB_ID; temp = CreateThreadPoolJob( job, tp->lastJobId, tp ); if( temp == NULL ) { ithread_mutex_unlock( &tp->mutex ); return rc; } if( job->priority == HIGH_PRIORITY ) { if( ListAddTail( &tp->highJobQ, temp ) ) { rc = 0; } } else if( job->priority == MED_PRIORITY ) { if( ListAddTail( &tp->medJobQ, temp ) ) { rc = 0; } } else { if( ListAddTail( &tp->lowJobQ, temp ) ) { rc = 0; } } // AddWorker if appropriate AddWorker( tp ); // Notify a waiting thread if( rc == 0 ) { ithread_cond_signal( &tp->condition ); } else { FreeThreadPoolJob( tp, temp ); } *jobId = tp->lastJobId++; ithread_mutex_unlock( &tp->mutex ); return rc; }
/**************************************************************************** * Function: WorkerThread * * Description: * Implements a thread pool worker. * Worker waits for a job to become available. * Worker picks up persistent jobs first, high priority, med priority, * then low priority. * If worker remains idle for more than specified max, the worker * is released. * Internal Only. * Parameters: * void * arg -> is cast to ThreadPool * *****************************************************************************/ static void *WorkerThread( void *arg ) { time_t start = 0; ThreadPoolJob *job = NULL; ListNode *head = NULL; struct timespec timeout; int retCode = 0; int persistent = -1; ThreadPool *tp = ( ThreadPool *) arg; // allow static linking #ifdef WIN32 #ifdef PTW32_STATIC_LIB pthread_win32_thread_attach_np(); #endif #endif assert( tp != NULL ); // Increment total thread count ithread_mutex_lock( &tp->mutex ); tp->totalThreads++; ithread_cond_broadcast( &tp->start_and_shutdown ); ithread_mutex_unlock( &tp->mutex ); SetSeed(); StatsTime( &start ); while( 1 ) { ithread_mutex_lock( &tp->mutex ); if( job ) { FreeThreadPoolJob( tp, job ); job = NULL; } retCode = 0; tp->stats.idleThreads++; tp->stats.totalWorkTime += ( StatsTime( NULL ) - start ); // work time StatsTime( &start ); // idle time if( persistent == 1 ) { // Persistent thread // becomes a regular thread tp->persistentThreads--; } if( persistent == 0 ) { tp->stats.workerThreads--; } // Check for a job or shutdown while( tp->lowJobQ.size == 0 && tp->medJobQ.size == 0 && tp->highJobQ.size == 0 && !tp->persistentJob && !tp->shutdown ) { // If wait timed out // and we currently have more than the // min threads, or if we have more than the max threads // (only possible if the attributes have been reset) // let this thread die. if( ( retCode == ETIMEDOUT && tp->totalThreads > tp->attr.minThreads ) || ( tp->attr.maxThreads != -1 && tp->totalThreads > tp->attr.maxThreads ) ) { tp->stats.idleThreads--; tp->totalThreads--; ithread_cond_broadcast( &tp->start_and_shutdown ); ithread_mutex_unlock( &tp->mutex ); #ifdef WIN32 #ifdef PTW32_STATIC_LIB // allow static linking pthread_win32_thread_detach_np (); #endif #endif return NULL; } SetRelTimeout( &timeout, tp->attr.maxIdleTime ); // wait for a job up to the specified max time retCode = ithread_cond_timedwait( &tp->condition, &tp->mutex, &timeout ); } tp->stats.idleThreads--; tp->stats.totalIdleTime += ( StatsTime( NULL ) - start ); // idle time StatsTime( &start ); // work time // bump priority of starved jobs BumpPriority( tp ); // if shutdown then stop if( tp->shutdown ) { tp->totalThreads--; ithread_cond_broadcast( &tp->start_and_shutdown ); ithread_mutex_unlock( &tp->mutex ); #ifdef WIN32 #ifdef PTW32_STATIC_LIB // allow static linking pthread_win32_thread_detach_np (); #endif #endif return NULL; } else { // Pick up persistent job if available if( tp->persistentJob ) { job = tp->persistentJob; tp->persistentJob = NULL; tp->persistentThreads++; persistent = 1; ithread_cond_broadcast( &tp->start_and_shutdown ); } else { tp->stats.workerThreads++; persistent = 0; // Pick the highest priority job if( tp->highJobQ.size > 0 ) { head = ListHead( &tp->highJobQ ); job = ( ThreadPoolJob *) head->item; CalcWaitTime( tp, HIGH_PRIORITY, job ); ListDelNode( &tp->highJobQ, head, 0 ); } else if( tp->medJobQ.size > 0 ) { head = ListHead( &tp->medJobQ ); job = ( ThreadPoolJob *) head->item; CalcWaitTime( tp, MED_PRIORITY, job ); ListDelNode( &tp->medJobQ, head, 0 ); } else if( tp->lowJobQ.size > 0 ) { head = ListHead( &tp->lowJobQ ); job = ( ThreadPoolJob *) head->item; CalcWaitTime( tp, LOW_PRIORITY, job ); ListDelNode( &tp->lowJobQ, head, 0 ); } else { // Should never get here assert( 0 ); tp->stats.workerThreads--; tp->totalThreads--; ithread_cond_broadcast( &tp->start_and_shutdown ); ithread_mutex_unlock( &tp->mutex ); return NULL; } } } ithread_mutex_unlock( &tp->mutex ); if( SetPriority( job->priority ) != 0 ) { // In the future can log // info } else { // In the future can log // info } // run the job job->func( job->arg ); // return to Normal SetPriority( DEFAULT_PRIORITY ); } }
/**************************************************************************** * Function: ThreadPoolRemove * * Description: * Removes a job from the thread pool. * Can only remove jobs which are not * currently running. * Parameters: * tp - valid thread pool pointer * jobId - id of job * ThreadPoolJob *out - space for removed job. * Can be null if not needed. * * Returns: * 0 on success. INVALID_JOB_ID on failure. *****************************************************************************/ int ThreadPoolRemove( ThreadPool *tp, int jobId, ThreadPoolJob *out ) { ThreadPoolJob *temp = NULL; int ret = INVALID_JOB_ID; ListNode *tempNode = NULL; ThreadPoolJob dummy; assert( tp != NULL ); if( tp == NULL ) { return EINVAL; } if( out == NULL ) { out = &dummy; } dummy.jobId = jobId; ithread_mutex_lock( &tp->mutex ); tempNode = ListFind( &tp->highJobQ, NULL, &dummy ); if( tempNode ) { temp = (ThreadPoolJob *)tempNode->item; *out = *temp; ListDelNode( &tp->highJobQ, tempNode, 0 ); FreeThreadPoolJob( tp, temp ); ithread_mutex_unlock( &tp->mutex ); return 0; } tempNode = ListFind( &tp->medJobQ, NULL, &dummy ); if( tempNode ) { temp = (ThreadPoolJob *)tempNode->item; *out = *temp; ListDelNode( &tp->medJobQ, tempNode, 0 ); FreeThreadPoolJob( tp, temp ); ithread_mutex_unlock( &tp->mutex ); return 0; } tempNode = ListFind( &tp->lowJobQ, NULL, &dummy ); if( tempNode ) { temp = (ThreadPoolJob *)tempNode->item; *out = *temp; ListDelNode( &tp->lowJobQ, tempNode, 0 ); FreeThreadPoolJob( tp, temp ); ithread_mutex_unlock( &tp->mutex ); return 0; } if( tp->persistentJob && tp->persistentJob->jobId == jobId ) { *out = *tp->persistentJob; FreeThreadPoolJob( tp, tp->persistentJob ); tp->persistentJob = NULL; ithread_mutex_unlock( &tp->mutex ); return 0; } ithread_mutex_unlock( &tp->mutex ); return ret; }