2011-11-05 16:35:31 +04:00
|
|
|
/* ********************************
|
2014-12-29 19:21:01 +03:00
|
|
|
* Author: Johan Hanssen Seferidis
|
|
|
|
* Date: 12/08/2011
|
|
|
|
* License: MIT
|
2014-12-29 14:48:08 +03:00
|
|
|
* Description: Library providing a threading pool where you can add
|
2014-12-29 19:21:01 +03:00
|
|
|
* work. For an example on usage refer to the main file
|
|
|
|
* found in the same package
|
2014-12-29 14:48:08 +03:00
|
|
|
*
|
2011-11-05 16:35:31 +04:00
|
|
|
*//** @file thpool.h *//*
|
|
|
|
********************************/
|
|
|
|
|
2014-12-31 15:09:10 +03:00
|
|
|
#include <unistd.h>
|
2011-11-05 16:35:31 +04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <semaphore.h>
|
|
|
|
#include <errno.h>
|
2014-12-31 15:09:10 +03:00
|
|
|
#include <time.h>
|
2011-11-05 16:35:31 +04:00
|
|
|
|
2014-12-29 19:21:01 +03:00
|
|
|
|
2014-12-30 15:28:43 +03:00
|
|
|
#include "thpool.h" /* here you can also find the interface to each function */
|
2014-12-30 15:56:19 +03:00
|
|
|
|
2014-12-29 19:21:01 +03:00
|
|
|
#define POLLING_INTERVAL 1
|
2014-12-30 15:56:19 +03:00
|
|
|
|
2014-12-29 16:14:45 +03:00
|
|
|
static int thpool_keepalive = 1;
|
2011-11-05 16:35:31 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
2014-12-30 15:56:19 +03:00
|
|
|
|
|
|
|
|
2011-11-05 16:35:31 +04:00
|
|
|
/* Initialise thread pool */
|
|
|
|
thpool_t* thpool_init(int threadsN){
|
2014-12-31 15:09:10 +03:00
|
|
|
|
|
|
|
if (threadsN < 0) {
|
|
|
|
threadsN = 0;
|
|
|
|
}
|
2011-11-05 16:35:31 +04:00
|
|
|
|
|
|
|
/* Make new thread pool */
|
2014-12-31 15:09:10 +03:00
|
|
|
thpool_t* thpool;
|
2014-12-30 15:32:15 +03:00
|
|
|
thpool=(thpool_t*)malloc(sizeof(thpool_t));
|
|
|
|
if (thpool==NULL){
|
2011-11-05 16:35:31 +04:00
|
|
|
fprintf(stderr, "thpool_init(): Could not allocate memory for thread pool\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-12-30 15:32:15 +03:00
|
|
|
thpool->threads=(pthread_t*)malloc(threadsN*sizeof(pthread_t));
|
|
|
|
if (thpool->threads==NULL){
|
2014-12-31 15:09:10 +03:00
|
|
|
fprintf(stderr, "thpool_init(): Could not allocate memory for threads\n");
|
2011-11-05 16:35:31 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-12-30 15:32:15 +03:00
|
|
|
thpool->threadsN=threadsN;
|
2014-12-31 15:09:10 +03:00
|
|
|
|
|
|
|
|
2011-11-05 16:35:31 +04:00
|
|
|
/* Initialise the job queue */
|
2014-12-30 15:32:15 +03:00
|
|
|
if (jobqueue_init(thpool)==-1){
|
2011-11-05 16:35:31 +04:00
|
|
|
fprintf(stderr, "thpool_init(): Could not allocate memory for job queue\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-12-31 15:09:10 +03:00
|
|
|
|
|
|
|
|
2011-11-05 16:35:31 +04:00
|
|
|
/* Make threads in pool */
|
2014-12-31 15:09:10 +03:00
|
|
|
int n;
|
|
|
|
for (n=0; n<threadsN; n++){
|
|
|
|
printf("Created thread %d in pool \n", n);
|
|
|
|
pthread_create(&(thpool->threads[n]), NULL, (void *)thpool_thread_do, (thpool_t*)thpool);
|
2011-11-05 16:35:31 +04:00
|
|
|
}
|
|
|
|
|
2014-12-30 15:32:15 +03:00
|
|
|
return thpool;
|
2011-11-05 16:35:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* What each individual thread is doing
|
2014-12-31 15:09:10 +03:00
|
|
|
* */
|
2014-12-30 15:32:15 +03:00
|
|
|
static void thpool_thread_do(thpool_t* thpool){
|
2014-12-29 19:21:01 +03:00
|
|
|
|
2011-11-05 16:35:31 +04:00
|
|
|
while(thpool_keepalive){
|
2014-12-29 16:14:45 +03:00
|
|
|
|
2014-12-30 15:32:15 +03:00
|
|
|
bsem_wait(thpool->jobqueue->has_jobs);
|
2011-11-05 16:35:31 +04:00
|
|
|
|
|
|
|
if (thpool_keepalive){
|
2014-12-29 16:14:45 +03:00
|
|
|
|
2011-11-05 16:35:31 +04:00
|
|
|
/* Read job from queue and execute it */
|
|
|
|
void*(*func_buff)(void* arg);
|
|
|
|
void* arg_buff;
|
2014-12-30 15:32:15 +03:00
|
|
|
job_t* job;
|
|
|
|
pthread_mutex_lock(&thpool->rwmutex);
|
|
|
|
job = jobqueue_pull(thpool);
|
|
|
|
pthread_mutex_unlock(&thpool->rwmutex);
|
|
|
|
if (job) {
|
2014-12-31 15:09:10 +03:00
|
|
|
printf("%u: Will run pulled job\n", (int)pthread_self());
|
|
|
|
func_buff = job->function;
|
|
|
|
arg_buff = job->arg;
|
2014-12-29 19:21:01 +03:00
|
|
|
func_buff(arg_buff);
|
2014-12-30 15:32:15 +03:00
|
|
|
free(job);
|
2014-12-29 16:14:45 +03:00
|
|
|
}
|
2014-12-31 15:09:10 +03:00
|
|
|
|
2011-11-05 16:35:31 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Add work to the thread pool */
|
2014-12-30 15:32:15 +03:00
|
|
|
int thpool_add_work(thpool_t* thpool, void *(*function_p)(void*), void* arg_p){
|
2014-12-31 15:09:10 +03:00
|
|
|
job_t* newjob;
|
2011-11-05 16:35:31 +04:00
|
|
|
|
2014-12-31 15:09:10 +03:00
|
|
|
newjob=(job_t*)malloc(sizeof(job_t));
|
|
|
|
if (newjob==NULL){
|
2011-11-05 16:35:31 +04:00
|
|
|
fprintf(stderr, "thpool_add_work(): Could not allocate memory for new job\n");
|
2014-12-31 15:09:10 +03:00
|
|
|
return -1;
|
2011-11-05 16:35:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* add function and argument */
|
2014-12-31 15:09:10 +03:00
|
|
|
newjob->function=function_p;
|
|
|
|
newjob->arg=arg_p;
|
2011-11-05 16:35:31 +04:00
|
|
|
|
|
|
|
/* add job to queue */
|
2014-12-30 15:32:15 +03:00
|
|
|
pthread_mutex_lock(&thpool->rwmutex);
|
2014-12-31 15:09:10 +03:00
|
|
|
jobqueue_push(thpool, newjob);
|
2014-12-30 15:32:15 +03:00
|
|
|
pthread_mutex_unlock(&thpool->rwmutex);
|
2014-12-29 16:23:44 +03:00
|
|
|
|
2014-12-29 19:21:01 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-29 19:42:27 +03:00
|
|
|
/* Wait until all jobs in queue have finished */
|
2014-12-30 15:32:15 +03:00
|
|
|
void thpool_wait(thpool_t* thpool){
|
|
|
|
while (thpool->jobqueue->len > 0) {
|
2014-12-29 19:21:01 +03:00
|
|
|
sleep(POLLING_INTERVAL);
|
|
|
|
}
|
2011-11-05 16:35:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Destroy the threadpool */
|
2014-12-30 15:32:15 +03:00
|
|
|
void thpool_destroy(thpool_t* thpool){
|
2014-12-30 15:28:43 +03:00
|
|
|
|
|
|
|
/* End each thread's infinite loop */
|
|
|
|
thpool_keepalive = 0;
|
|
|
|
|
2014-12-31 15:09:10 +03:00
|
|
|
/* Kill idle threads */
|
|
|
|
double TIMEOUT = 1.0;
|
|
|
|
time_t start,end;
|
|
|
|
double tpassed;
|
|
|
|
time (&start);
|
|
|
|
while (tpassed < TIMEOUT){
|
|
|
|
bsem_post(thpool->jobqueue->has_jobs);
|
|
|
|
time (&end);
|
|
|
|
tpassed = difftime(end,start);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait for working threads to finish their work*/
|
|
|
|
int n;
|
|
|
|
for (n=0; n < (thpool->threadsN); n++){
|
|
|
|
pthread_join(thpool->threads[n], NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
//sleep(2);
|
2014-12-29 19:42:27 +03:00
|
|
|
|
2014-12-31 15:09:10 +03:00
|
|
|
/* Job queue cleanup */
|
|
|
|
pthread_mutex_lock(&thpool->rwmutex);
|
|
|
|
jobqueue_destroy(thpool);
|
|
|
|
pthread_mutex_unlock(&thpool->rwmutex);
|
|
|
|
//free(thpool->jobqueue);
|
|
|
|
|
|
|
|
|
2011-11-05 16:35:31 +04:00
|
|
|
/* Dealloc */
|
2014-12-30 15:32:15 +03:00
|
|
|
//free(thpool->threads);
|
2014-12-31 15:09:10 +03:00
|
|
|
//free(thpool);
|
|
|
|
printf("DEALLOC finito\n");
|
|
|
|
|
2011-11-05 16:35:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-12-30 15:56:19 +03:00
|
|
|
/* ===================== JOB QUEUE OPERATIONS ======================= */
|
|
|
|
|
2011-11-05 16:35:31 +04:00
|
|
|
|
|
|
|
/* Initialise queue */
|
2014-12-30 15:32:15 +03:00
|
|
|
static int jobqueue_init(thpool_t* thpool){
|
|
|
|
thpool->jobqueue=(jobqueue_t*)malloc(sizeof(jobqueue_t));
|
2014-12-31 15:09:10 +03:00
|
|
|
if (thpool->jobqueue==NULL){
|
|
|
|
return -1;
|
|
|
|
}
|
2014-12-30 15:32:15 +03:00
|
|
|
thpool->jobqueue->has_jobs = (bsem_t*)malloc(sizeof(bsem_t));
|
2014-12-31 15:09:10 +03:00
|
|
|
jobqueue_clear(thpool);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Clear queue */
|
|
|
|
static void jobqueue_clear(thpool_t* thpool){
|
|
|
|
|
|
|
|
job_t* curjob;
|
|
|
|
curjob = thpool->jobqueue->front;
|
2014-12-30 15:28:43 +03:00
|
|
|
|
2014-12-31 15:09:10 +03:00
|
|
|
//printf("JOBS: %d\n", thpool->jobqueue->len);
|
|
|
|
//printf("rear: %p\n", thpool->jobqueue->rear);
|
|
|
|
//printf("curjob prev: %p\n", thpool->jobqueue->rear->prev);
|
|
|
|
//thpool->jobqueue->tail = curjob->prev;
|
2014-12-31 15:09:58 +03:00
|
|
|
//while(thpool->jobqueue->len){
|
2014-12-31 15:09:10 +03:00
|
|
|
// thpool->jobqueue->tail = curjob->prev;
|
|
|
|
// free(curjob);
|
|
|
|
// curjob=thpool->jobqueue->tail;
|
|
|
|
//}
|
|
|
|
|
|
|
|
thpool->jobqueue->front = NULL;
|
|
|
|
thpool->jobqueue->rear = NULL;
|
|
|
|
thpool->jobqueue->has_jobs->v = 0;
|
2014-12-30 15:32:15 +03:00
|
|
|
thpool->jobqueue->len = 0;
|
2014-12-31 15:09:10 +03:00
|
|
|
|
2011-11-05 16:35:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Add job to queue */
|
2014-12-30 15:32:15 +03:00
|
|
|
static void jobqueue_push(thpool_t* thpool, job_t* newjob){ /* remember that job prev and next point to NULL */
|
2014-12-29 19:21:01 +03:00
|
|
|
|
2014-12-31 15:09:10 +03:00
|
|
|
newjob->prev = NULL;
|
2011-11-05 16:35:31 +04:00
|
|
|
|
2014-12-30 15:32:15 +03:00
|
|
|
switch(thpool->jobqueue->len){
|
2014-11-23 14:38:34 +03:00
|
|
|
|
|
|
|
case 0: /* if there are no jobs in queue */
|
2014-12-31 15:09:10 +03:00
|
|
|
thpool->jobqueue->front = newjob;
|
|
|
|
thpool->jobqueue->rear = newjob;
|
2011-11-05 16:35:31 +04:00
|
|
|
break;
|
|
|
|
|
2014-11-23 14:38:34 +03:00
|
|
|
default: /* if there are already jobs in queue */
|
2014-12-31 15:09:10 +03:00
|
|
|
thpool->jobqueue->rear->prev = newjob;
|
|
|
|
thpool->jobqueue->rear = newjob;
|
|
|
|
|
2014-11-23 14:38:34 +03:00
|
|
|
}
|
2014-12-30 15:32:15 +03:00
|
|
|
thpool->jobqueue->len++;
|
|
|
|
bsem_post(thpool->jobqueue->has_jobs);
|
2011-11-05 16:35:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-23 14:38:34 +03:00
|
|
|
/* Get first element from queue */
|
2014-12-30 15:32:15 +03:00
|
|
|
static job_t* jobqueue_pull(thpool_t* thpool){
|
2014-12-31 15:09:10 +03:00
|
|
|
|
2014-12-29 19:21:01 +03:00
|
|
|
/* get first job */
|
2014-12-30 15:32:15 +03:00
|
|
|
job_t* job;
|
2014-12-31 15:09:10 +03:00
|
|
|
job = thpool->jobqueue->front;
|
2014-11-23 14:38:34 +03:00
|
|
|
|
|
|
|
/* remove job from queue */
|
2014-12-30 15:32:15 +03:00
|
|
|
switch(thpool->jobqueue->len){
|
2011-11-05 16:35:31 +04:00
|
|
|
|
2014-11-23 14:38:34 +03:00
|
|
|
case 0: /* if there are no jobs in queue */
|
|
|
|
return NULL;
|
2011-11-05 16:35:31 +04:00
|
|
|
|
2014-11-23 14:38:34 +03:00
|
|
|
case 1: /* if there is only one job in queue */
|
2014-12-31 15:09:10 +03:00
|
|
|
thpool->jobqueue->front = NULL;
|
|
|
|
thpool->jobqueue->rear = NULL;
|
2011-11-05 16:35:31 +04:00
|
|
|
break;
|
2014-12-31 15:09:10 +03:00
|
|
|
|
|
|
|
default: /* if there are more than two jobs in queue */
|
|
|
|
thpool->jobqueue->front = job->prev;
|
2011-11-05 16:35:31 +04:00
|
|
|
|
|
|
|
}
|
2014-12-30 15:32:15 +03:00
|
|
|
thpool->jobqueue->len--;
|
2014-12-29 19:21:01 +03:00
|
|
|
|
|
|
|
// Make sure has_jobs has right value
|
2014-12-30 15:32:15 +03:00
|
|
|
if (thpool->jobqueue->len > 0) {
|
|
|
|
bsem_post(thpool->jobqueue->has_jobs);
|
2014-12-29 19:21:01 +03:00
|
|
|
}
|
2014-12-30 15:28:43 +03:00
|
|
|
|
2014-12-30 15:32:15 +03:00
|
|
|
return job;
|
2011-11-05 16:35:31 +04:00
|
|
|
}
|
|
|
|
|
2014-11-23 14:38:34 +03:00
|
|
|
|
2011-11-05 16:35:31 +04:00
|
|
|
/* Remove and deallocate all jobs in queue */
|
2014-12-31 15:09:10 +03:00
|
|
|
static void jobqueue_destroy(thpool_t* thpool){
|
2011-11-05 16:35:31 +04:00
|
|
|
|
2014-12-31 15:09:10 +03:00
|
|
|
jobqueue_clear(thpool);
|
2014-12-29 16:14:45 +03:00
|
|
|
|
|
|
|
/* Deallocs */
|
2014-12-30 15:32:15 +03:00
|
|
|
free(thpool->jobqueue->has_jobs);
|
2011-11-05 16:35:31 +04:00
|
|
|
}
|
2014-12-29 19:21:01 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
2014-12-30 15:56:19 +03:00
|
|
|
|
|
|
|
|
2014-12-29 19:42:27 +03:00
|
|
|
/* ======================== SYNCHRONISATION ========================= */
|
2014-12-29 19:21:01 +03:00
|
|
|
|
|
|
|
|
2014-12-30 15:56:19 +03:00
|
|
|
/* Binary semaphore post */
|
2014-12-29 19:42:27 +03:00
|
|
|
static void bsem_post(bsem_t *bsem) {
|
2014-12-30 15:28:43 +03:00
|
|
|
pthread_mutex_lock(&bsem->mutex);
|
|
|
|
bsem->v = 1;
|
|
|
|
pthread_cond_signal(&bsem->cond);
|
|
|
|
pthread_mutex_unlock(&bsem->mutex);
|
2014-12-29 19:21:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-30 15:56:19 +03:00
|
|
|
/* Binary semaphore wait */
|
2014-12-29 19:42:27 +03:00
|
|
|
static void bsem_wait(bsem_t *bsem) {
|
2014-12-30 15:28:43 +03:00
|
|
|
pthread_mutex_lock(&bsem->mutex);
|
|
|
|
while (bsem->v != 1) {
|
|
|
|
pthread_cond_wait(&bsem->cond, &bsem->mutex);
|
|
|
|
}
|
|
|
|
bsem->v = 0;
|
|
|
|
pthread_mutex_unlock(&bsem->mutex);
|
2014-12-29 19:21:01 +03:00
|
|
|
}
|