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