#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "thread.h" #include "malloc.h" #include "once.h" #include "macro.h" struct sa_thread { pthread_t id; sa_thread_func_t thread_func; void *userdata; int running; pthread_mutex_t running_mutex; }; struct sa_tls { pthread_key_t key; }; static sa_tls_t *thread_tls; static sa_once_t thread_tls_once = SA_ONCE_INIT; static void tls_free_cb(void *p) { sa_thread_t *t = p; sa_assert(t); if (!t->thread_func) /* This is a foreign thread, we need to free the struct */ sa_free(t); } static void thread_tls_once_func(void) { thread_tls = sa_tls_new(tls_free_cb); } static void* internal_thread_func(void *userdata) { sa_thread_t *t = userdata; sa_assert(t); t->id = pthread_self(); sa_tls_set(thread_tls, t); t->thread_func(t->userdata); sa_assert_success(pthread_mutex_lock(&t->running_mutex)); t->running = 0; sa_assert_success(pthread_mutex_unlock(&t->running_mutex)); return NULL; } sa_thread_t* sa_thread_new(sa_thread_func_t thread_func, void *userdata) { sa_thread_t *t; sa_assert(thread_func); if (sa_once(&thread_tls_once, thread_tls_once_func) < 0 || !thread_tls) return NULL; if (!(t = sa_new(sa_thread_t, 1))) return NULL; t->thread_func = thread_func; t->userdata = userdata; sa_assert_success(pthread_mutex_init(&t->running_mutex, NULL)); t->running = 1; if (pthread_create(&t->id, NULL, internal_thread_func, t) < 0) { sa_assert_success(pthread_mutex_destroy(&t->running_mutex)); sa_free(t); return NULL; } return t; } int sa_thread_is_running(sa_thread_t *t) { int b; sa_assert(t); if (!t->thread_func) { /* Mhmm, this is a foreign thread, t->running is not * necessarily valid. We misuse pthread_getschedparam() to * check if the thread is valid. This might not be portable. */ int policy; struct sched_param param; return pthread_getschedparam(t->id, &policy, ¶m) >= 0 || errno != ESRCH; } sa_assert_success(pthread_mutex_lock(&t->running_mutex)); b = t->running; sa_assert_success(pthread_mutex_unlock(&t->running_mutex)); return !!b; } void sa_thread_free(sa_thread_t *t) { sa_assert(t); sa_thread_join(t); sa_assert_success(pthread_mutex_destroy(&t->running_mutex)); sa_free(t); } int sa_thread_join(sa_thread_t *t) { sa_assert(t); return pthread_join(t->id, NULL); } sa_thread_t* sa_thread_self(void) { sa_thread_t *t; if (sa_once(&thread_tls_once, thread_tls_once_func) < 0 || !thread_tls) return NULL; if ((t = sa_tls_get(thread_tls))) return t; /* This is a foreign thread, let's create a pthread structure to * make sure that we can always return a sensible pointer */ if (!(t = sa_new(sa_thread_t, 1))) return NULL; t->id = pthread_self(); t->thread_func = NULL; t->userdata = NULL; t->running = 1; sa_tls_set(thread_tls, t); return t; } void* sa_thread_get_data(sa_thread_t *t) { sa_assert(t); return t->userdata; } void sa_thread_set_data(sa_thread_t *t, void *userdata) { sa_assert(t); t->userdata = userdata; } void sa_thread_yield(void) { #ifdef HAVE_PTHREAD_YIELD pthread_yield(); #else sa_assert_success(sched_yield()); #endif } sa_tls_t* sa_tls_new(sa_free_func_t free_cb) { sa_tls_t *t; if (!(t = sa_new(sa_tls_t, 1))) return NULL; if (pthread_key_create(&t->key, free_cb) < 0) { sa_free(t); return NULL; } return t; } void sa_tls_free(sa_tls_t *t) { sa_assert(t); sa_assert_success(pthread_key_delete(t->key)); sa_free(t); } void *sa_tls_get(sa_tls_t *t) { sa_assert(t); return pthread_getspecific(t->key); } void *sa_tls_set(sa_tls_t *t, void *userdata) { void *r; r = pthread_getspecific(t->key); sa_assert_success(pthread_setspecific(t->key, userdata)); return r; }