/*** This file is part of libsydney. Copyright 2007-2008 Lennart Poettering libsydney is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. libsydney is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with libsydney. If not, see . ***/ #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; sa_bool_t joined; }; 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; t->joined = FALSE; 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); if (t->joined) return -1; t->joined = TRUE; 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->joined = TRUE; 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; }