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
39 struct 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
48 struct pa_tls {
49 pthread_key_t key;
50 };
51
thread_free_cb(void * p)52 static 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
64 PA_STATIC_TLS_DECLARE(current_thread, thread_free_cb);
65
internal_thread_func(void * userdata)66 static 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
pa_thread_new(const char * name,pa_thread_func_t thread_func,void * userdata)87 pa_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
pa_thread_is_running(pa_thread * t)107 int 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
pa_thread_free(pa_thread * t)119 void 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
pa_thread_free_nojoin(pa_thread * t)128 void pa_thread_free_nojoin(pa_thread *t) {
129 pa_assert(t);
130
131 pa_xfree(t->name);
132 pa_xfree(t);
133 }
134
pa_thread_join(pa_thread * t)135 int 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
pa_thread_self(void)146 pa_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
pa_thread_get_data(pa_thread * t)165 void* pa_thread_get_data(pa_thread *t) {
166 pa_assert(t);
167
168 return t->userdata;
169 }
170
pa_thread_set_data(pa_thread * t,void * userdata)171 void pa_thread_set_data(pa_thread *t, void *userdata) {
172 pa_assert(t);
173
174 t->userdata = userdata;
175 }
176
pa_thread_set_name(pa_thread * t,const char * name)177 void 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
pa_thread_get_name(pa_thread * t)190 const 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
pa_thread_yield(void)214 void 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
pa_tls_new(pa_free_cb_t free_cb)222 pa_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
pa_tls_free(pa_tls * t)235 void 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
pa_tls_get(pa_tls * t)242 void *pa_tls_get(pa_tls *t) {
243 pa_assert(t);
244
245 return pthread_getspecific(t->key);
246 }
247
pa_tls_set(pa_tls * t,void * userdata)248 void *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