libresman

annotate src/threadpool.c @ 6:410c19c735b2

- removed the glew dependency - initial thread pool implementation
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 03 Feb 2014 05:22:09 +0200
parents
children 03f3de659c32
rev   line source
nuclear@6 1 #include <stdio.h>
nuclear@6 2 #include <stdlib.h>
nuclear@6 3 #include <string.h>
nuclear@6 4 #include <pthread.h>
nuclear@6 5 #include "threadpool.h"
nuclear@6 6
nuclear@6 7 struct work_item {
nuclear@6 8 void *data;
nuclear@6 9 struct work_item *next;
nuclear@6 10 };
nuclear@6 11
nuclear@6 12 struct thread_pool {
nuclear@6 13 pthread_t *workers;
nuclear@6 14 int num_workers;
nuclear@6 15
nuclear@6 16 pthread_mutex_t work_lock;
nuclear@6 17 pthread_cond_t work_cond;
nuclear@6 18
nuclear@6 19 tpool_work_func work_func;
nuclear@6 20 void *cls;
nuclear@6 21
nuclear@6 22 struct work_item *work_list, *work_list_tail;
nuclear@6 23 int work_count;
nuclear@6 24 };
nuclear@6 25
nuclear@6 26
nuclear@6 27 static void *thread_func(void *tp);
nuclear@6 28 static struct work_item *alloc_node(void);
nuclear@6 29 static void free_node(struct work_item *node);
nuclear@6 30 static int get_processor_count(void);
nuclear@6 31
nuclear@6 32
nuclear@6 33
nuclear@6 34 int tpool_init(struct thread_pool *tpool, int num_threads)
nuclear@6 35 {
nuclear@6 36 int i;
nuclear@6 37
nuclear@6 38 memset(tpool, 0, sizeof *tpool);
nuclear@6 39
nuclear@6 40 if(num_threads <= 0) {
nuclear@6 41 num_threads = get_processor_count();
nuclear@6 42 }
nuclear@6 43 tpool->num_workers = num_threads;
nuclear@6 44
nuclear@6 45 printf("initializing thread pool with %d worker threads\n", num_threads);
nuclear@6 46
nuclear@6 47 for(i=0; i<num_threads; i++) {
nuclear@6 48 if(pthread_create(tpool->workers + i, 0, thread_func, tpool) == -1) {
nuclear@6 49 fprintf(stderr, "%s: failed to create thread %d\n", __FUNCTION__, i);
nuclear@6 50 tpool_destroy(tpool);
nuclear@6 51 return -1;
nuclear@6 52 }
nuclear@6 53 }
nuclear@6 54
nuclear@6 55 pthread_mutex_init(&tpool->work_lock, 0);
nuclear@6 56 pthread_cond_init(&tpool->work_cond, 0);
nuclear@6 57 return 0;
nuclear@6 58 }
nuclear@6 59
nuclear@6 60 void tpool_destroy(struct thread_pool *tpool)
nuclear@6 61 {
nuclear@6 62 int i;
nuclear@6 63 for(i=0; i<tpool->num_workers; i++) {
nuclear@6 64 void *ret;
nuclear@6 65 pthread_join(tpool->workers[i], &ret);
nuclear@6 66 }
nuclear@6 67
nuclear@6 68 pthread_mutex_destroy(&tpool->work_lock);
nuclear@6 69 pthread_cond_destroy(&tpool->work_cond);
nuclear@6 70 }
nuclear@6 71
nuclear@6 72 void tpool_set_work_func(struct thread_pool *tpool, tpool_work_func func, void *cls)
nuclear@6 73 {
nuclear@6 74 tpool->work_func = func;
nuclear@6 75 tpool->cls = cls;
nuclear@6 76 }
nuclear@6 77
nuclear@6 78 int tpool_add_work(struct thread_pool *tpool, void *data)
nuclear@6 79 {
nuclear@6 80 struct work_item *node;
nuclear@6 81
nuclear@6 82 if(!(node = alloc_node())) {
nuclear@6 83 fprintf(stderr, "%s: failed to allocate new work item node\n", __FUNCTION__);
nuclear@6 84 return -1;
nuclear@6 85 }
nuclear@6 86 node->data = data;
nuclear@6 87 node->next = 0;
nuclear@6 88
nuclear@6 89 pthread_mutex_lock(&tpool->work_lock);
nuclear@6 90
nuclear@6 91 if(!tpool->work_list) {
nuclear@6 92 tpool->work_list = tpool->work_list_tail = node;
nuclear@6 93 } else {
nuclear@6 94 tpool->work_list_tail->next = node;
nuclear@6 95 tpool->work_list_tail = node;
nuclear@6 96 }
nuclear@6 97
nuclear@6 98 pthread_mutex_unlock(&tpool->work_lock);
nuclear@6 99 return 0;
nuclear@6 100 }
nuclear@6 101
nuclear@6 102
nuclear@6 103 static void *thread_func(void *tp)
nuclear@6 104 {
nuclear@6 105 struct work_item *job;
nuclear@6 106 struct thread_pool *tpool = tp;
nuclear@6 107
nuclear@6 108 pthread_mutex_lock(&tpool->work_lock);
nuclear@6 109 for(;;) {
nuclear@6 110 /* while there aren't any work items to do go to sleep on the condvar */
nuclear@6 111 pthread_cond_wait(&tpool->work_cond, &tpool->work_lock);
nuclear@6 112 if(!tpool->work_list) {
nuclear@6 113 continue; /* spurious wakeup, go back to sleep */
nuclear@6 114 }
nuclear@6 115
nuclear@6 116 job = tpool->work_list;
nuclear@6 117 tpool->work_list = tpool->work_list->next;
nuclear@6 118
nuclear@6 119 tpool->work_func(job->data, tpool->cls);
nuclear@6 120 }
nuclear@6 121 pthread_mutex_unlock(&tpool->work_lock);
nuclear@6 122 return 0;
nuclear@6 123 }
nuclear@6 124
nuclear@6 125 /* TODO: custom allocator */
nuclear@6 126 static struct work_item *alloc_node(void)
nuclear@6 127 {
nuclear@6 128 return malloc(sizeof(struct work_item));
nuclear@6 129 }
nuclear@6 130
nuclear@6 131 static void free_node(struct work_item *node)
nuclear@6 132 {
nuclear@6 133 free(node);
nuclear@6 134 }
nuclear@6 135
nuclear@6 136 /* The following highly platform-specific code detects the number
nuclear@6 137 * of processors available in the system. It's used by the thread pool
nuclear@6 138 * to autodetect how many threads to spawn.
nuclear@6 139 * Currently works on: Linux, BSD, Darwin, and Windows.
nuclear@6 140 */
nuclear@6 141
nuclear@6 142 #if defined(__APPLE__) && defined(__MACH__)
nuclear@6 143 # ifndef __unix__
nuclear@6 144 # define __unix__ 1
nuclear@6 145 # endif /* unix */
nuclear@6 146 # ifndef __bsd__
nuclear@6 147 # define __bsd__ 1
nuclear@6 148 # endif /* bsd */
nuclear@6 149 #endif /* apple */
nuclear@6 150
nuclear@6 151 #if defined(unix) || defined(__unix__)
nuclear@6 152 #include <unistd.h>
nuclear@6 153
nuclear@6 154 # ifdef __bsd__
nuclear@6 155 # include <sys/sysctl.h>
nuclear@6 156 # endif
nuclear@6 157 #endif
nuclear@6 158
nuclear@6 159 #if defined(WIN32) || defined(__WIN32__)
nuclear@6 160 #include <windows.h>
nuclear@6 161 #endif
nuclear@6 162
nuclear@6 163
nuclear@6 164 static int get_processor_count(void)
nuclear@6 165 {
nuclear@6 166 #if defined(unix) || defined(__unix__)
nuclear@6 167 # if defined(__bsd__)
nuclear@6 168 /* BSD systems provide the num.processors through sysctl */
nuclear@6 169 int num, mib[] = {CTL_HW, HW_NCPU};
nuclear@6 170 size_t len = sizeof num;
nuclear@6 171
nuclear@6 172 sysctl(mib, 2, &num, &len, 0, 0);
nuclear@6 173 return num;
nuclear@6 174
nuclear@6 175 # elif defined(__sgi)
nuclear@6 176 /* SGI IRIX flavour of the _SC_NPROC_ONLN sysconf */
nuclear@6 177 return sysconf(_SC_NPROC_ONLN);
nuclear@6 178 # else
nuclear@6 179 /* Linux (and others?) have the _SC_NPROCESSORS_ONLN sysconf */
nuclear@6 180 return sysconf(_SC_NPROCESSORS_ONLN);
nuclear@6 181 # endif /* bsd/sgi/other */
nuclear@6 182
nuclear@6 183 #elif defined(WIN32) || defined(__WIN32__)
nuclear@6 184 /* under windows we need to call GetSystemInfo */
nuclear@6 185 SYSTEM_INFO info;
nuclear@6 186 GetSystemInfo(&info);
nuclear@6 187 return info.dwNumberOfProcessors;
nuclear@6 188 #endif
nuclear@6 189 }