#include "common.h" #include "thread.h" #include #include #if !HAVE_PTHREAD_YIELD #include #endif #if !HAVE_CLOCK_GETTIME # include #endif #ifdef DEBUG #define ABORTFAIL(x) \ do { \ if ((x)) abort(); \ } while (false) #else #define ABORTFAIL(x) (x) #endif bool thread_new(thread_run_t run, void* userdata) { pthread_t thread; pthread_attr_t attr; assert(run); if (pthread_attr_init(&attr)) { return NULL; } if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) { pthread_attr_destroy(&attr); return NULL; } if (pthread_create(&thread, &attr, run, userdata)) { pthread_attr_destroy(&attr); return false; } pthread_attr_destroy(&attr); return true; } struct thread_t { pthread_t thread; }; thread_t* thread_joinable_new(thread_run_t run, void* userdata) { thread_t* ret = malloc(sizeof(thread_t)); pthread_attr_t attr; assert(run); if (!ret) { return NULL; } if (pthread_attr_init(&attr)) { free(ret); return NULL; } if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE)) { pthread_attr_destroy(&attr); free(ret); return NULL; } if (pthread_create(&ret->thread, &attr, run, userdata)) { pthread_attr_destroy(&attr); free(ret); return NULL; } pthread_attr_destroy(&attr); return ret; } void* thread_join(thread_t* thread) { void* ret; assert(thread); ABORTFAIL(pthread_join(thread->thread, &ret)); free(thread); return ret; } struct thread_id_t { pthread_t thread; }; thread_id_t* thread_id_new(thread_t* thread) { thread_id_t* id = malloc(sizeof(thread_id_t)); if (id) { id->thread = thread ? thread->thread : pthread_self(); } return id; } void thread_id_free(thread_id_t* id) { free(id); } bool thread_id_is_current(thread_id_t* id) { assert(id); return id->thread == pthread_self(); } void thread_yield(void) { #if HAVE_PTHREAD_YIELD # if PTHREAD_YIELD_VOID pthread_yield(); # else ABORTFAIL(pthread_yield()); # endif #else ABORTFAIL(sched_yield()); #endif } struct thread_lock_t { pthread_mutex_t mutex; }; thread_lock_t* thread_lock_new(void) { thread_lock_t* lock = malloc(sizeof(thread_lock_t)); if (!lock) { return lock; } for (;;) { #ifdef DEBUG pthread_mutexattr_t attr; if (pthread_mutexattr_init(&attr)) { break; } pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); if (pthread_mutex_init(&lock->mutex, &attr)) { pthread_mutexattr_destroy(&attr); break; } pthread_mutexattr_destroy(&attr); #else if (pthread_mutex_init(&lock->mutex, NULL)) { break; } #endif return lock; } free(lock); return NULL; } thread_lock_t* thread_lock_recursive_new(void) { thread_lock_t* lock = malloc(sizeof(thread_lock_t)); for (;;) { if (!lock) { break; } pthread_mutexattr_t attr; if (pthread_mutexattr_init(&attr)) { break; } pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); if (pthread_mutex_init(&lock->mutex, &attr)) { pthread_mutexattr_destroy(&attr); break; } pthread_mutexattr_destroy(&attr); return lock; } free(lock); return NULL; } void thread_lock_free(thread_lock_t* lock) { if (lock) { pthread_mutex_destroy(&lock->mutex); free(lock); } } void thread_lock_lock(thread_lock_t* lock) { assert(lock); ABORTFAIL(pthread_mutex_lock(&lock->mutex)); } void thread_lock_unlock(thread_lock_t* lock) { assert(lock); ABORTFAIL(pthread_mutex_unlock(&lock->mutex)); } struct thread_cond_t { pthread_cond_t cond; }; thread_cond_t* thread_cond_new(void) { thread_cond_t* cond = malloc(sizeof(thread_cond_t)); if (cond) { if (pthread_cond_init(&cond->cond, NULL)) { free(cond); return NULL; } } return cond; } void thread_cond_free(thread_cond_t* cond) { if (cond) { pthread_cond_destroy(&cond->cond); free(cond); } } void thread_cond_wait(thread_cond_t* cond, thread_lock_t* lock) { assert(cond); assert(lock); ABORTFAIL(pthread_cond_wait(&cond->cond, &lock->mutex)); } bool thread_cond_timedwait(thread_cond_t* cond, thread_lock_t* lock, const struct timespec *abstime) { int ret; assert(cond); assert(lock); assert(abstime); ret = pthread_cond_timedwait(&cond->cond, &lock->mutex, abstime); if (ret == 0) { return true; } #ifdef DEBUG if (ret != ETIMEDOUT) { abort(); } #endif return false; } void thread_cond_signal(thread_cond_t* cond) { assert(cond); ABORTFAIL(pthread_cond_signal(&cond->cond)); } void thread_cond_broadcast(thread_cond_t* cond) { ABORTFAIL(pthread_cond_broadcast(&cond->cond)); } void thread_abstime(struct timespec *abstime, unsigned long add_ms) { #if HAVE_CLOCK_GETTIME clock_gettime(CLOCK_REALTIME, abstime); #else { struct timeval tv; gettimeofday(&tv, NULL); abstime->tv_sec = tv.tv_sec; abstime->tv_nsec = tv.tv_usec * 1000; } #endif timespec_addms(abstime, add_ms); } struct thread_data_t { pthread_key_t key; }; thread_data_t *thread_data_new(thread_data_value_free_t free_value) { thread_data_t *data = malloc(sizeof(thread_data_t)); if (data) { if (pthread_key_create(&data->key, free_value)) { free(data); return NULL; } } return data; } void thread_data_free(thread_data_t *data) { if (data) { pthread_key_delete(data->key); free(data); } } void *thread_data_value(thread_data_t *data) { assert(data); return pthread_getspecific(data->key); } void thread_data_set(thread_data_t *data, void *value) { assert(data); ABORTFAIL(pthread_setspecific(data->key, value)); } void thread_once(thread_once_t *once, thread_run_once_t run) { assert(once && run); ABORTFAIL(pthread_once(once, run)); }