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