#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "malloc.h" #include "mutex.h" #include "macro.h" #include "thread.h" struct sa_thread { pthread_t id; sa_thread_func_t thread_func; void *userdata; int running; sa_mutex *mutex; }; struct sa_tls { pthread_key_t key; }; static pthread_once_t tid_once = PTHREAD_ONCE_INIT; static sa_tls *tid_tls = NULL; static void tid_free_cb(void *p) { sa_thread *t = p; sa_assert(t); if (!t->thread_func) { /* This is a foreign thread, we need to free the struct */ sa_mutex_free(t->mutex); sa_free(t); } } static void tid_init(void) { tid_tls = sa_tls_new(tid_free_cb); } static void* internal_thread_func(void *userdata) { sa_thread *t = userdata; sa_assert(t); t->id = pthread_self(); sa_tls_set(tid_tls, t); sa_mutex_lock(t->mutex); t->running++; sa_mutex_unlock(t->mutex); t->thread_func(t->userdata); sa_mutex_lock(t->mutex); t->running -= 2; sa_mutex_unlock(t->mutex); return NULL; } sa_thread* sa_thread_new(sa_thread_func_t thread_func, void *userdata) { sa_thread *t; sa_assert(thread_func); pthread_once(&tid_once, tid_init); if (!tid_tls) return NULL; if (!(t = sa_new(sa_thread, 1))) return NULL; t->thread_func = thread_func; t->userdata = userdata; t->running = 1; if (!(t->mutex = sa_mutex_new(FALSE, FALSE))) { sa_free(t); return NULL; } if (pthread_create(&t->id, NULL, internal_thread_func, t) < 0) { sa_mutex_free(t->mutex); sa_free(t); return NULL; } return t; } sa_bool_t sa_thread_is_running(sa_thread *t) { sa_bool_t b; sa_assert(t); /* Unfortunately there is no way to tell whether a "foreign" * thread is still running. See * http://udrepper.livejournal.com/16844.html for more * information */ sa_assert(t->thread_func); sa_mutex_lock(t->mutex); b = t->running > 0; sa_mutex_unlock(t->mutex); return b; } void sa_thread_free(sa_thread *t) { sa_assert(t); /* Only allowed when this is not a foreign thread */ sa_assert(t->thread_func); sa_thread_join(t); sa_mutex_free(t->mutex); sa_free(t); } int sa_thread_join(sa_thread *t) { sa_assert(t); /* Only allowed when this is not a foreign thread */ sa_assert(t->thread_func); return pthread_join(t->id, NULL); } sa_thread* sa_thread_self(void) { sa_thread *t; pthread_once(&tid_once, tid_init); if (!tid_tls) return NULL; if ((t = sa_tls_get(tid_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, 1))) return NULL; t->id = pthread_self(); t->thread_func = NULL; t->userdata = NULL; t->running = 2; t->mutex = sa_mutex_new(FALSE, FALSE); sa_tls_set(tid_tls, t); return t; } void* sa_thread_get_data(sa_thread *t) { sa_assert(t); return t->userdata; } void sa_thread_set_data(sa_thread *t, void *userdata) { sa_assert(t); t->userdata = userdata; } void sa_thread_yield(void) { #ifdef HAVE_PTHREAD_YIELD pthread_yield(); #else sa_assert_se(sched_yield() == 0); #endif } sa_tls* sa_tls_new(sa_free_cb_t free_cb) { sa_tls *t; if (!(t = sa_new(sa_tls, 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) { sa_assert(t); sa_assert_se(pthread_key_delete(t->key) == 0); sa_free(t); } void *sa_tls_get(sa_tls *t) { sa_assert(t); return pthread_getspecific(t->key); } void *sa_tls_set(sa_tls *t, void *userdata) { void *r; sa_assert(t); r = pthread_getspecific(t->key); sa_assert_se(pthread_setspecific(t->key, userdata) == 0); return r; }