Gerard-Meier

ThreadPool V2

May 31st, 2012
63
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 5.75 KB | None | 0 0
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <pthread.h>
  4. #include <assert.h>
  5.  
  6. // Used for deadlines, -1 indicates one does not care when it runs.
  7. #define DONTCARE -1
  8.  
  9. struct Task;
  10.  
  11. // Inspired by epoll:
  12. typedef union {
  13.     int integer;
  14.     void* ptr;
  15. } TaskData;
  16.  
  17. typedef struct {
  18.     TaskData data;
  19.     time_t addedTime;
  20.     time_t deadline;
  21.     int isRunning;
  22.  
  23.     void (*runnable) (TaskData);
  24.  
  25.     struct Task* next;
  26.     struct Task* prev;
  27. } Task;
  28.  
  29. typedef struct {
  30.     int workerCount;
  31.     pthread_t workers[30]; // Hardcoded length *sigh*
  32.     pthread_mutex_t popMutex;
  33.     pthread_mutex_t pushMutex;
  34.  
  35.     Task* chainHead; // pop here
  36.  
  37.     pthread_t scheduler;
  38.     pthread_cond_t emptyCond;
  39. } ThreadPool;
  40.  
  41. Task* popTaskWait(ThreadPool* pool) {
  42.  
  43.     // No popping while popping: (adding is fine)
  44.     pthread_mutex_lock(&pool->popMutex);
  45.  
  46.     if(pool->chainHead == NULL) {
  47.         pthread_cond_wait(&pool->emptyCond, &pool->popMutex);
  48.        
  49.     }
  50.  
  51.     assert(pool->chainHead != NULL);
  52.  
  53.     // Ok something just became available. First make
  54.     // sure no one is adding more tasks:
  55.     pthread_mutex_lock(&pool->pushMutex);
  56.  
  57.     Task* task = pool->chainHead;
  58.  
  59.     if(task->next == NULL) {
  60.         pool->chainHead = NULL;
  61.     } else {
  62.         pool->chainHead = task->next;
  63.     }
  64.  
  65.     task->next = NULL;
  66.     task->prev = NULL;
  67.  
  68.     // Release locks, any one can add or pop tasks again:
  69.     pthread_mutex_unlock(&pool->pushMutex);
  70.     pthread_mutex_unlock(&pool->popMutex);
  71.  
  72.     return task;
  73. }
  74.  
  75. void* singlerun(void* voidTask) {
  76.     Task* task = (Task*) voidTask;
  77.     task->isRunning = 1;
  78.     task->runnable(task->data);
  79. }
  80.  
  81. void* scheduler(void* voidPool) {
  82.     ThreadPool* pool = (ThreadPool*) voidPool;
  83.     int i;
  84.  
  85.     while(1) {
  86.         time_t now = time(NULL);
  87.  
  88.         // TODO: is this really the best approach? Seems rather blocking...
  89.         pthread_mutex_lock(&pool->pushMutex);
  90.         pthread_mutex_lock(&pool->popMutex);
  91.  
  92.        
  93.         Task* tmp = pool->chainHead;
  94.         while(tmp != NULL) {
  95.             //printf("cjeck \n");
  96.             if(!tmp->isRunning && tmp->deadline != DONTCARE) {
  97.                 time_t delta = now - tmp->addedTime;
  98.                 if(delta >= tmp->deadline) {
  99.                     printf("Running task after exceeding a %d of %d seconds deadline. \n", delta, tmp->deadline);
  100.  
  101.                     // This is the first item:
  102.                     if(tmp == pool->chainHead) {
  103.                         pool->chainHead = tmp->next;
  104.                        
  105.                         if(tmp->next != NULL) {
  106.                             pool->chainHead->prev = NULL;
  107.                         }
  108.                     } else {
  109.                         Task* parent = tmp->prev;
  110.                         Task* child  = tmp->next;
  111.                         parent->next = child;
  112.  
  113.                         // We may not have one to begin with: (this was the last in the linked list)
  114.                         if(child != NULL) {
  115.                             child->prev  = parent;
  116.                         }
  117.                     }
  118.  
  119.                     pthread_t id;
  120.                     pthread_create(&id, NULL, singlerun, (void*)tmp);
  121.  
  122.                 }
  123.             }
  124.  
  125.             tmp = tmp->next;
  126.         }
  127.  
  128.         pthread_mutex_unlock(&pool->pushMutex);
  129.         pthread_mutex_unlock(&pool->popMutex);
  130.  
  131.         usleep(1000000);
  132.     }
  133.  
  134.     assert(0 == 1);
  135. }
  136.  
  137.  
  138.  
  139. void* worker(void* voidPool) {
  140.     ThreadPool* pool = (ThreadPool*) voidPool;
  141.  
  142.     while(1) {
  143.         singlerun((void*) popTaskWait(pool));
  144.     }
  145.  
  146.     assert(0 == 1);
  147. }
  148.  
  149. void addTask(ThreadPool* pool, Task* task) {
  150.     task->next      = NULL;
  151.     task->prev      = NULL;
  152.     task->isRunning = 0;
  153.     task->addedTime = time(NULL);
  154.  
  155.     pthread_mutex_lock(&pool->pushMutex);
  156.    
  157.     if(pool->chainHead == NULL) {
  158.  
  159.         pool->chainHead = task;
  160.     } else {
  161.         Task* tmp = pool->chainHead;
  162.  
  163.         while(tmp->next != NULL) {
  164.             tmp = tmp->next;
  165.         }
  166.  
  167.         tmp->next  = task;
  168.         task->prev = tmp;
  169.     }
  170.  
  171.  
  172.     pthread_cond_signal(&pool->emptyCond);
  173.     pthread_mutex_unlock(&pool->pushMutex);
  174. }
  175.  
  176. ThreadPool* createPool(const int numWorkers) {
  177.     int i;
  178.     ThreadPool* pool    = malloc(sizeof(ThreadPool));
  179.     pool->workerCount   = 0;
  180.     pool->scheduler     = 0;
  181.     pool->chainHead     = NULL;
  182.  
  183.     pthread_mutex_init(&pool->popMutex,  NULL);
  184.     pthread_mutex_init(&pool->pushMutex, NULL);
  185.     pthread_cond_init(&pool->emptyCond,  NULL);
  186.  
  187.     pthread_create(&pool->scheduler, NULL, &scheduler, (void*)pool);
  188.  
  189.     for(i = 0; i < numWorkers; ++i) {
  190.         pthread_create(&pool->workers[pool->workerCount++], NULL, &worker, (void*)pool);
  191.     }
  192.  
  193.     printf("Created a threadpool with %i workers. \n", numWorkers);
  194.  
  195.     return pool;
  196. }
  197.  
  198. void joinThreadPool(ThreadPool* pool) {
  199.     int i;
  200.  
  201.     for(i = 0; i < pool->workerCount; ++i) {
  202.         pthread_join(pool->workers[i], NULL);
  203.     }
  204. }
  205.  
  206. void starve(TaskData data) {
  207.     printf("Starting really long task. \n");
  208.     sleep(10);
  209.     printf("Finished really long task. \n");
  210. }
  211.  
  212. void echoTest(TaskData data) {
  213.     printf("Running a really quick task... \n");
  214. }
  215.  
  216. int main(int argc, char** argv) {
  217.     int i;
  218.     ThreadPool* pool = createPool(5); // Intentionally ignoring the CORE * 2 + 1 rule.
  219.  
  220.     Task task2[10];
  221.  
  222.     for(i = 0; i < 10; ++i) {
  223.         task2[i].deadline = DONTCARE;
  224.         task2[i].runnable = &starve;
  225.         addTask(pool, &task2[i]);
  226.     }
  227.  
  228.     Task task[2];
  229.     for(i = 0; i < 2; ++i) {
  230.         task[i].deadline = 4; // in seconds.
  231.         task[i].runnable = &echoTest;
  232.         addTask(pool, &task[i]);
  233.     }
  234.  
  235.     joinThreadPool(pool);
  236.  
  237.     free(pool);
  238.  
  239.     return 0;
  240. }
Advertisement
Add Comment
Please, Sign In to add comment