1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2006 Lennart Poettering 5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB 6 7 PulseAudio is free software; you can redistribute it and/or modify 8 it under the terms of the GNU Lesser General Public License as published 9 by the Free Software Foundation; either version 2.1 of the License, 10 or (at your option) any later version. 11 12 PulseAudio is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU Lesser General Public License 18 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 19***/ 20 21#ifdef HAVE_CONFIG_H 22#include <config.h> 23#endif 24 25#include <pthread.h> 26#include <sched.h> 27#include <errno.h> 28 29#ifdef __linux__ 30#include <sys/prctl.h> 31#endif 32 33#include <pulse/xmalloc.h> 34#include <pulsecore/atomic.h> 35#include <pulsecore/macro.h> 36 37#include "thread.h" 38 39struct pa_thread { 40 pthread_t id; 41 pa_thread_func_t thread_func; 42 void *userdata; 43 pa_atomic_t running; 44 bool joined; 45 char *name; 46}; 47 48struct pa_tls { 49 pthread_key_t key; 50}; 51 52static void thread_free_cb(void *p) { 53 pa_thread *t = p; 54 55 pa_assert(t); 56 57 if (!t->thread_func) { 58 /* This is a foreign thread, we need to free the struct */ 59 pa_xfree(t->name); 60 pa_xfree(t); 61 } 62} 63 64PA_STATIC_TLS_DECLARE(current_thread, thread_free_cb); 65 66static void* internal_thread_func(void *userdata) { 67 pa_thread *t = userdata; 68 pa_assert(t); 69 70#ifdef __linux__ 71 prctl(PR_SET_NAME, t->name); 72#elif defined(HAVE_PTHREAD_SETNAME_NP) && defined(OS_IS_DARWIN) 73 pthread_setname_np(t->name); 74#endif 75 76 t->id = pthread_self(); 77 78 PA_STATIC_TLS_SET(current_thread, t); 79 80 pa_atomic_inc(&t->running); 81 t->thread_func(t->userdata); 82 pa_atomic_sub(&t->running, 2); 83 84 return NULL; 85} 86 87pa_thread* pa_thread_new(const char *name, pa_thread_func_t thread_func, void *userdata) { 88 pa_thread *t; 89 90 pa_assert(thread_func); 91 92 t = pa_xnew0(pa_thread, 1); 93 t->name = pa_xstrdup(name); 94 t->thread_func = thread_func; 95 t->userdata = userdata; 96 97 if (pthread_create(&t->id, NULL, internal_thread_func, t) < 0) { 98 pa_xfree(t); 99 return NULL; 100 } 101 102 pa_atomic_inc(&t->running); 103 104 return t; 105} 106 107int pa_thread_is_running(pa_thread *t) { 108 pa_assert(t); 109 110 /* Unfortunately there is no way to tell whether a "foreign" 111 * thread is still running. See 112 * http://udrepper.livejournal.com/16844.html for more 113 * information */ 114 pa_assert(t->thread_func); 115 116 return pa_atomic_load(&t->running) > 0; 117} 118 119void pa_thread_free(pa_thread *t) { 120 pa_assert(t); 121 122 pa_thread_join(t); 123 124 pa_xfree(t->name); 125 pa_xfree(t); 126} 127 128void pa_thread_free_nojoin(pa_thread *t) { 129 pa_assert(t); 130 131 pa_xfree(t->name); 132 pa_xfree(t); 133} 134 135int pa_thread_join(pa_thread *t) { 136 pa_assert(t); 137 pa_assert(t->thread_func); 138 139 if (t->joined) 140 return -1; 141 142 t->joined = true; 143 return pthread_join(t->id, NULL); 144} 145 146pa_thread* pa_thread_self(void) { 147 pa_thread *t; 148 149 if ((t = PA_STATIC_TLS_GET(current_thread))) 150 return t; 151 152 /* This is a foreign thread, let's create a pthread structure to 153 * make sure that we can always return a sensible pointer */ 154 155 t = pa_xnew0(pa_thread, 1); 156 t->id = pthread_self(); 157 t->joined = true; 158 pa_atomic_store(&t->running, 2); 159 160 PA_STATIC_TLS_SET(current_thread, t); 161 162 return t; 163} 164 165void* pa_thread_get_data(pa_thread *t) { 166 pa_assert(t); 167 168 return t->userdata; 169} 170 171void pa_thread_set_data(pa_thread *t, void *userdata) { 172 pa_assert(t); 173 174 t->userdata = userdata; 175} 176 177void pa_thread_set_name(pa_thread *t, const char *name) { 178 pa_assert(t); 179 180 pa_xfree(t->name); 181 t->name = pa_xstrdup(name); 182 183#ifdef __linux__ 184 prctl(PR_SET_NAME, name); 185#elif defined(HAVE_PTHREAD_SETNAME_NP) && defined(OS_IS_DARWIN) 186 pthread_setname_np(name); 187#endif 188} 189 190const char *pa_thread_get_name(pa_thread *t) { 191 pa_assert(t); 192 193#ifdef __linux__ 194 if (!t->name) { 195 t->name = pa_xmalloc(17); 196 197 if (prctl(PR_GET_NAME, t->name) >= 0) 198 t->name[16] = 0; 199 else { 200 pa_xfree(t->name); 201 t->name = NULL; 202 } 203 } 204#elif defined(HAVE_PTHREAD_GETNAME_NP) && defined(OS_IS_DARWIN) 205 if (!t->name) { 206 t->name = pa_xmalloc0(17); 207 pthread_getname_np(t->id, t->name, 16); 208 } 209#endif 210 211 return t->name; 212} 213 214void pa_thread_yield(void) { 215#ifdef HAVE_PTHREAD_YIELD 216 pthread_yield(); 217#else 218 pa_assert_se(sched_yield() == 0); 219#endif 220} 221 222pa_tls* pa_tls_new(pa_free_cb_t free_cb) { 223 pa_tls *t; 224 225 t = pa_xnew(pa_tls, 1); 226 227 if (pthread_key_create(&t->key, free_cb) < 0) { 228 pa_xfree(t); 229 return NULL; 230 } 231 232 return t; 233} 234 235void pa_tls_free(pa_tls *t) { 236 pa_assert(t); 237 238 pa_assert_se(pthread_key_delete(t->key) == 0); 239 pa_xfree(t); 240} 241 242void *pa_tls_get(pa_tls *t) { 243 pa_assert(t); 244 245 return pthread_getspecific(t->key); 246} 247 248void *pa_tls_set(pa_tls *t, void *userdata) { 249 void *r; 250 251 r = pthread_getspecific(t->key); 252 pa_assert_se(pthread_setspecific(t->key, userdata) == 0); 253 return r; 254} 255