00001
00002 #include <microfeed-common/microfeedthread.h>
00003 #include <microfeed-common/microfeedmisc.h>
00004 #include <microfeed-common/microfeedstore.h>
00005
00006 #include <pthread.h>
00007 #include <signal.h>
00008
00009 struct _MicrofeedThread {
00010 MicrofeedThread* next;
00011 MicrofeedThread* previous;
00012
00013 unsigned int reference_count;
00014 MicrofeedMutex* mutex;
00015 unsigned long id;
00016
00017 MicrofeedThreadPool* thread_pool;
00018 void* thread_implementation;
00019 MicrofeedThreadFunction function;
00020 void* in_data;
00021 void* out_data;
00022 MicrofeedThreadExitCallback exit_callback;
00023 void* user_data;
00024 };
00025
00026 struct _MicrofeedThreadPool {
00027 unsigned int started_threads;
00028 unsigned int max_threads;
00029 MicrofeedThreadExitCallback exit_callback;
00030 void* user_data;
00031 MicrofeedMutex* mutex;
00032 MicrofeedStore* waiting_threads;
00033 };
00034
00035 struct _MicrofeedMutex {
00036 void* mutex_implementation;
00037 };
00038
00039 static void* default_thread_new(MicrofeedThreadFunction function, void* data);
00040 static void default_thread_free(void* thread_implementation);
00041 static void* default_thread_get_current(void);
00042 static void default_thread_send_signal(void* thread_implementation, int signal_number);
00043 static void default_thread_join(void* thread_implementation);
00044 static void* default_mutex_new(void);
00045 static void default_mutex_free(void* mutex_implementation);
00046 static void default_mutex_lock(void* mutex_implementation);
00047 static void default_mutex_unlock(void* mutex_implementation);
00048 static void* thread_function(void* data);
00049
00050 MicrofeedThreadFunctions functions = {
00051 default_thread_new,
00052 default_thread_free,
00053 default_thread_get_current,
00054 default_thread_send_signal,
00055 default_thread_join,
00056 default_mutex_new,
00057 default_mutex_free,
00058 default_mutex_lock,
00059 default_mutex_unlock
00060 };
00061
00062 static MicrofeedThread* threads = NULL;
00063 static MicrofeedMutex* threads_mutex = NULL;
00064 static unsigned long thread_count = 0;
00065
00071 void microfeed_thread_set_functions(MicrofeedThreadFunctions* thread_functions) {
00072 functions = *thread_functions;
00073 }
00074
00075 void microfeed_thread_init(void) {
00076 if (!threads_mutex) {
00077 threads_mutex = microfeed_mutex_new();
00078 }
00079 if (!threads) {
00080 threads = microfeed_thread_get_current();
00081 }
00082 }
00083
00084 void microfeed_thread_cleanup(void) {
00085 void* current_thread_implementation;
00086 MicrofeedThread* thread;
00087
00088 microfeed_mutex_lock(threads_mutex);
00089
00090 current_thread_implementation = functions.thread_get_current();
00091
00092 do {
00093 for (thread = threads; thread; thread = thread->next) {
00094 if (thread->thread_implementation && thread->thread_implementation != current_thread_implementation) {
00095 break;
00096 }
00097 }
00098 if (thread) {
00099 microfeed_mutex_unlock(threads_mutex);
00100
00101 microfeed_thread_join(thread);
00102
00103 microfeed_mutex_lock(threads_mutex);
00104 }
00105 } while (thread);
00106
00107 microfeed_mutex_unlock(threads_mutex);
00108 }
00109
00110 MicrofeedThread* microfeed_thread_new(MicrofeedThreadFunction function, void* data) {
00111
00112 return microfeed_thread_new_with_exit_callback(function, data, NULL, NULL);
00113 }
00114
00115 MicrofeedThread* microfeed_thread_new_with_exit_callback(MicrofeedThreadFunction function, void* data, MicrofeedThreadExitCallback exit_callback, void* user_data) {
00116 MicrofeedThread* thread;
00117
00118 thread = microfeed_memory_allocate(MicrofeedThread);
00119 thread->reference_count = 2;
00120 thread->mutex = microfeed_mutex_new();
00121 thread->function = function;
00122 thread->in_data = data;
00123 thread->out_data= NULL;
00124 thread->exit_callback = exit_callback;
00125 thread->user_data = user_data;
00126
00127 microfeed_mutex_lock(threads_mutex);
00128
00129 thread->id = ++thread_count;
00130
00131 thread->next = threads;
00132 if (thread->next) {
00133 thread->next->previous = thread;
00134 }
00135 threads = thread;
00136
00137 microfeed_mutex_lock(thread->mutex);
00138
00139 thread->thread_implementation = functions.thread_new(thread_function, thread);
00140
00141 microfeed_mutex_unlock(thread->mutex);
00142
00143 microfeed_mutex_unlock(threads_mutex);
00144
00145 return thread;
00146 }
00147
00148 void microfeed_thread_free(MicrofeedThread* thread) {
00149 if (thread) {
00150 functions.thread_free(thread->thread_implementation);
00151
00152 microfeed_mutex_lock(threads_mutex);
00153
00154 if (thread->previous) {
00155 thread->previous->next = thread->next;
00156 } else if (threads) {
00157 threads = thread->next;
00158 }
00159 if (thread->next) {
00160 thread->next->previous = thread->previous;
00161 }
00162
00163 microfeed_mutex_unlock(threads_mutex);
00164
00165 microfeed_memory_free(thread);
00166 }
00167 }
00168
00169 MicrofeedThread* microfeed_thread_ref(MicrofeedThread* thread) {
00170 microfeed_mutex_lock(thread->mutex);
00171
00172 thread->reference_count++;
00173
00174 microfeed_mutex_unlock(thread->mutex);
00175
00176 return thread;
00177 }
00178
00179 void microfeed_thread_unref(MicrofeedThread* thread) {
00180 microfeed_mutex_lock(thread->mutex);
00181
00182 thread->reference_count--;
00183 if (thread->reference_count == 0) {
00184 microfeed_thread_free(thread);
00185 } else {
00186
00187 microfeed_mutex_unlock(thread->mutex);
00188 }
00189 }
00190
00191 MicrofeedThread* microfeed_thread_get_current(void) {
00192 MicrofeedThread* thread = NULL;
00193 void* thread_implementation;
00194
00195 microfeed_mutex_lock(threads_mutex);
00196
00197 if ((thread_implementation = functions.thread_get_current())) {
00198 for (thread = threads; thread; thread = thread->next) {
00199 if (thread->thread_implementation == thread_implementation) {
00200 break;
00201 }
00202 }
00203 }
00204 if (!thread) {
00205 thread = microfeed_memory_allocate(MicrofeedThread);
00206 thread->reference_count = 1;
00207 thread->mutex = microfeed_mutex_new();
00208 thread->id = 0;
00209 thread->function = NULL;
00210 thread->in_data = NULL;
00211 thread->out_data= NULL;
00212
00213 thread->next = threads;
00214 if (thread->next) {
00215 thread->next->previous = thread;
00216 }
00217 threads = thread;
00218 thread->thread_implementation = thread_implementation;
00219 }
00220
00221 microfeed_mutex_unlock(threads_mutex);
00222
00223 return thread;
00224 }
00225
00226 void microfeed_thread_send_signal(MicrofeedThread* thread, int signal_number) {
00227 functions.thread_send_signal(thread->thread_implementation, signal_number);
00228 }
00229
00230 void microfeed_thread_join(MicrofeedThread* thread) {
00231 functions.thread_join(thread->thread_implementation);
00232 }
00233
00234 unsigned long microfeed_thread_get_id(MicrofeedThread* thread) {
00235
00236 return thread->id;
00237 }
00238
00239
00240 MicrofeedThreadPool* microfeed_thread_pool_new(unsigned int maximum_thread_count) {
00241
00242 return microfeed_thread_pool_new_with_exit_callback(maximum_thread_count, NULL, NULL);
00243 }
00244
00250 MicrofeedThreadPool* microfeed_thread_pool_new_with_exit_callback(unsigned int maximum_thread_count, MicrofeedThreadExitCallback exit_callback, void* user_data) {
00251 MicrofeedThreadPool* thread_pool;
00252
00253 thread_pool = microfeed_memory_allocate(MicrofeedThreadPool);
00254 thread_pool->max_threads = maximum_thread_count;
00255 thread_pool->exit_callback = exit_callback;
00256 thread_pool->user_data = user_data;
00257 thread_pool->mutex = microfeed_mutex_new();
00258 thread_pool->waiting_threads = microfeed_store_new_unsorted(microfeed_store_compare_keys_direct, microfeed_store_get_key_direct);
00259
00260 return thread_pool;
00261
00262 }
00263
00264 MicrofeedThread* microfeed_thread_pool_queue_thread(MicrofeedThreadPool* thread_pool, MicrofeedThreadFunction function, void* data) {
00265
00266 return microfeed_thread_pool_queue_thread_with_exit_callback(thread_pool, function, data, NULL, NULL);
00267 }
00268
00269 MicrofeedThread* microfeed_thread_pool_queue_thread_with_exit_callback(MicrofeedThreadPool* thread_pool, MicrofeedThreadFunction function, void* data, MicrofeedThreadExitCallback exit_callback, void* user_data) {
00270 MicrofeedThread* thread;
00271
00272 thread = microfeed_memory_allocate(MicrofeedThread);
00273 thread->reference_count = 2;
00274 thread->mutex = microfeed_mutex_new();
00275 thread->thread_pool = thread_pool;
00276 thread->function = function;
00277 thread->in_data = data;
00278 thread->out_data= NULL;
00279 thread->exit_callback = exit_callback;
00280 thread->user_data = user_data;
00281
00282 microfeed_mutex_lock(thread_pool->mutex);
00283
00284 if (thread_pool->started_threads < thread_pool->max_threads) {
00285 thread_pool->started_threads++;
00286
00287 microfeed_mutex_lock(threads_mutex);
00288
00289 thread->next = threads;
00290 if (thread->next) {
00291 thread->next->previous = thread;
00292 }
00293 threads = thread;
00294
00295 microfeed_mutex_lock(thread->mutex);
00296
00297 thread->thread_implementation = functions.thread_new(thread_function, thread);
00298
00299 microfeed_mutex_unlock(thread->mutex);
00300
00301 microfeed_mutex_unlock(threads_mutex);
00302 } else {
00303 microfeed_store_insert(thread_pool->waiting_threads, thread);
00304 }
00305
00306 microfeed_mutex_unlock(thread_pool->mutex);
00307
00308 return thread;
00309 }
00310
00311 unsigned int microfeed_thread_pool_get_started_thread_count(MicrofeedThreadPool* thread_pool) {
00312
00313 return thread_pool->started_threads;
00314 }
00315
00316 unsigned int microfeed_thread_pool_get_waiting_thread_count(MicrofeedThreadPool* thread_pool) {
00317
00318 return microfeed_store_get_size(thread_pool->waiting_threads);
00319 }
00320
00321 void microfeed_thread_pool_set_maximum_thread_count(MicrofeedThreadPool* thread_pool, unsigned int maximum_thread_count) {
00322
00323 thread_pool->max_threads = maximum_thread_count;
00324 }
00325
00326 MicrofeedMutex* microfeed_mutex_new(void) {
00327 MicrofeedMutex* mutex;
00328
00329 mutex = microfeed_memory_allocate(MicrofeedMutex);
00330 mutex->mutex_implementation = functions.mutex_new();
00331
00332 return mutex;
00333 }
00334
00335 void microfeed_mutex_free(MicrofeedMutex* mutex) {
00336 functions.mutex_free(mutex->mutex_implementation);
00337 microfeed_memory_free(mutex);
00338 }
00339
00340 void microfeed_mutex_lock(MicrofeedMutex* mutex) {
00341 functions.mutex_lock(mutex->mutex_implementation);
00342 }
00343
00344 void microfeed_mutex_unlock(MicrofeedMutex* mutex) {
00345 functions.mutex_unlock(mutex->mutex_implementation);
00346 }
00347
00348 static void* default_thread_new(MicrofeedThreadFunction function, void* data) {
00349 pthread_t* thread_implementation;
00350
00351 thread_implementation = microfeed_memory_allocate(pthread_t);
00352 pthread_create(thread_implementation, NULL, (void* (*)(void*))function, data);
00353
00354 return (void*)thread_implementation;
00355 }
00356
00357 static void default_thread_free(void* thread_implementation) {
00358 microfeed_memory_free(thread_implementation);
00359 }
00360
00361 static void* default_thread_get_current(void) {
00362 pthread_t self;
00363 MicrofeedThread* thread;
00364 pthread_t* thread_implementation;
00365
00366 self = pthread_self();
00367
00368 for (thread = threads; thread; thread = thread->next) {
00369 if (*((pthread_t*)thread->thread_implementation) == self) {
00370 break;
00371 }
00372 }
00373 if (thread) {
00374 thread_implementation = thread->thread_implementation;
00375 } else {
00376 thread_implementation = microfeed_memory_allocate(pthread_t);
00377 *thread_implementation = self;
00378 }
00379
00380 return (void*)thread_implementation;
00381 }
00382
00383 static void default_thread_send_signal(void* thread_implementation, int signal_number) {
00384 pthread_kill(*(pthread_t*)thread_implementation, signal_number);
00385 }
00386
00387 static void default_thread_join(void* thread_implementation) {
00388 pthread_join(*(pthread_t*)thread_implementation, NULL);
00389 }
00390
00391 static void* default_mutex_new(void) {
00392 pthread_mutex_t* mutex_implementation;
00393
00394 mutex_implementation = microfeed_memory_allocate(pthread_mutex_t);
00395 pthread_mutex_init(mutex_implementation, NULL);
00396
00397 return (void*)mutex_implementation;
00398 }
00399
00400 static void default_mutex_free(void* mutex_implementation) {
00401 pthread_mutex_destroy((pthread_mutex_t*)mutex_implementation);
00402 microfeed_memory_free(mutex_implementation);
00403 }
00404
00405 static void default_mutex_lock(void* mutex_implementation) {
00406 pthread_mutex_lock((pthread_mutex_t*)mutex_implementation);
00407 }
00408
00409 static void default_mutex_unlock(void* mutex_implementation) {
00410 pthread_mutex_unlock((pthread_mutex_t*)mutex_implementation);
00411 }
00412
00413 static void* thread_function(void* data) {
00414 MicrofeedThread* thread;
00415 MicrofeedThreadPool* thread_pool;
00416
00417 thread = (MicrofeedThread*)data;
00418 thread_pool = thread->thread_pool;
00419
00420 do {
00421 thread->out_data = thread->function(thread->in_data);
00422
00423 if (thread->exit_callback) {
00424 thread->exit_callback(thread, thread->user_data);
00425 }
00426
00427 microfeed_mutex_lock(thread->mutex);
00428
00429 thread->reference_count--;
00430 if (thread->reference_count == 0) {
00431 microfeed_thread_free(thread);
00432 } else {
00433
00434
00435 microfeed_mutex_unlock(thread->mutex);
00436 }
00437
00438 thread = NULL;
00439
00440 if (thread_pool) {
00441 microfeed_mutex_lock(thread_pool->mutex);
00442
00443 if (thread_pool->started_threads <= thread_pool->max_threads && microfeed_store_get_size(thread_pool->waiting_threads) > 0) {
00444 thread = microfeed_store_remove_index(thread_pool->waiting_threads, 0, MicrofeedThread);
00445 } else {
00446 thread_pool->started_threads--;
00447 if (thread_pool->exit_callback) {
00448 thread_pool->exit_callback(thread, thread_pool->user_data);
00449 }
00450 }
00451
00452 microfeed_mutex_unlock(thread_pool->mutex);
00453 }
00454 } while (thread);
00455
00456 return NULL;
00457 }