int pthreadpool_finished_job(struct pthreadpool *pool) { int result, ret, fd; ssize_t nread; ret = pthread_mutex_lock(&pool->mutex); if (ret != 0) { errno = ret; return -1; } /* * Just some cleanup under the mutex */ pthreadpool_join_children(pool); fd = pool->sig_pipe[0]; ret = pthread_mutex_unlock(&pool->mutex); assert(ret == 0); if (fd == -1) { errno = EINVAL; return -1; } nread = -1; errno = EINTR; while ((nread == -1) && (errno == EINTR)) { nread = read(fd, &result, sizeof(int)); } /* * TODO: handle nread > 0 && nread < sizeof(int) */ /* * Lock the mutex to provide a memory barrier for data from the worker * thread to the main thread. The pipe access itself does not have to * be locked, for sizeof(int) the write to a pipe is atomic, and only * one thread reads from it. But we need to lock the mutex briefly * even if we don't do anything under the lock, to make sure we can * see all memory the helper thread has written. */ ret = pthread_mutex_lock(&pool->mutex); if (ret == -1) { errno = ret; return -1; } ret = pthread_mutex_unlock(&pool->mutex); assert(ret == 0); return result; }
int pthreadpool_add_job(struct pthreadpool *pool, int job_id, void (*fn)(void *private_data), void *private_data) { struct pthreadpool_job *job; pthread_t thread_id; int res; sigset_t mask, omask; job = (struct pthreadpool_job *)malloc(sizeof(struct pthreadpool_job)); if (job == NULL) { return ENOMEM; } job->fn = fn; job->private_data = private_data; job->id = job_id; job->next = NULL; res = pthread_mutex_lock(&pool->mutex); if (res != 0) { free(job); return res; } if (pool->shutdown) { /* * Protect against the pool being shut down while * trying to add a job */ res = pthread_mutex_unlock(&pool->mutex); assert(res == 0); free(job); return EINVAL; } /* * Just some cleanup under the mutex */ pthreadpool_join_children(pool); /* * Add job to the end of the queue */ if (pool->jobs == NULL) { pool->jobs = job; } else { pool->last_job->next = job; } pool->last_job = job; if (pool->num_idle > 0) { /* * We have idle threads, wake one. */ res = pthread_cond_signal(&pool->condvar); pthread_mutex_unlock(&pool->mutex); return res; } if ((pool->max_threads != 0) && (pool->num_threads >= pool->max_threads)) { /* * No more new threads, we just queue the request */ pthread_mutex_unlock(&pool->mutex); return 0; } /* * Create a new worker thread. It should not receive any signals. */ sigfillset(&mask); res = pthread_sigmask(SIG_BLOCK, &mask, &omask); if (res != 0) { pthread_mutex_unlock(&pool->mutex); return res; } res = pthread_create(&thread_id, NULL, pthreadpool_server, (void *)pool); if (res == 0) { pool->num_threads += 1; } assert(pthread_sigmask(SIG_SETMASK, &omask, NULL) == 0); pthread_mutex_unlock(&pool->mutex); return res; }
int pthreadpool_destroy(struct pthreadpool *pool) { int ret, ret1; ret = pthread_mutex_lock(&pool->mutex); if (ret != 0) { return ret; } if ((pool->jobs != NULL) || pool->shutdown) { ret = pthread_mutex_unlock(&pool->mutex); assert(ret == 0); return EBUSY; } if (pool->num_threads > 0) { /* * We have active threads, tell them to finish, wait for that. */ pool->shutdown = 1; if (pool->num_idle > 0) { /* * Wake the idle threads. They will find pool->quit to * be set and exit themselves */ ret = pthread_cond_broadcast(&pool->condvar); if (ret != 0) { pthread_mutex_unlock(&pool->mutex); return ret; } } while ((pool->num_threads > 0) || (pool->num_exited > 0)) { if (pool->num_exited > 0) { pthreadpool_join_children(pool); continue; } /* * A thread that shuts down will also signal * pool->condvar */ ret = pthread_cond_wait(&pool->condvar, &pool->mutex); if (ret != 0) { pthread_mutex_unlock(&pool->mutex); return ret; } } } ret = pthread_mutex_unlock(&pool->mutex); if (ret != 0) { return ret; } ret = pthread_mutex_destroy(&pool->mutex); ret1 = pthread_cond_destroy(&pool->condvar); if (ret != 0) { return ret; } if (ret1 != 0) { return ret1; } ret = pthread_mutex_lock(&pthreadpools_mutex); if (ret != 0) { return ret; } DLIST_REMOVE(pthreadpools, pool); ret = pthread_mutex_unlock(&pthreadpools_mutex); assert(ret == 0); close(pool->sig_pipe[0]); pool->sig_pipe[0] = -1; close(pool->sig_pipe[1]); pool->sig_pipe[1] = -1; free(pool->exited); free(pool); return 0; }
int pthreadpool_destroy(struct pthreadpool *pool) { int ret, ret1; ret = pthread_mutex_lock(&pool->mutex); if (ret != 0) { return ret; } if (pool->num_threads > 0) { /* * We have active threads, tell them to finish, wait for that. */ pool->shutdown = 1; if (pool->num_idle > 0) { /* * Wake the idle threads. They will find pool->quit to * be set and exit themselves */ ret = pthread_cond_broadcast(&pool->condvar); if (ret != 0) { pthread_mutex_unlock(&pool->mutex); return ret; } } while ((pool->num_threads > 0) || (pool->num_exited > 0)) { if (pool->num_exited > 0) { pthreadpool_join_children(pool); continue; } /* * A thread that shuts down will also signal * pool->condvar */ ret = pthread_cond_wait(&pool->condvar, &pool->mutex); if (ret != 0) { pthread_mutex_unlock(&pool->mutex); return ret; } } } ret = pthread_mutex_unlock(&pool->mutex); if (ret != 0) { return ret; } ret = pthread_mutex_destroy(&pool->mutex); ret1 = pthread_cond_destroy(&pool->condvar); if ((ret == 0) && (ret1 == 0)) { free(pool); } if (ret != 0) { return ret; } return ret1; }