summaryrefslogtreecommitdiffstats
path: root/thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'thread.c')
-rw-r--r--thread.c200
1 files changed, 200 insertions, 0 deletions
diff --git a/thread.c b/thread.c
new file mode 100644
index 0000000..806e210
--- /dev/null
+++ b/thread.c
@@ -0,0 +1,200 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pthread.h>
+#include <sched.h>
+#include <errno.h>
+
+#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, &param) >= 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;
+}
+