]> code.delx.au - pulseaudio/blob - src/pulsecore/thread-posix.c
thread-posix: Use pthread_(get|set)name_np() if available
[pulseaudio] / src / pulsecore / thread-posix.c
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, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <pthread.h>
28 #include <sched.h>
29 #include <errno.h>
30
31 #ifdef __linux__
32 #include <sys/prctl.h>
33 #endif
34
35 #include <pulse/xmalloc.h>
36 #include <pulsecore/mutex.h>
37 #include <pulsecore/once.h>
38 #include <pulsecore/atomic.h>
39 #include <pulsecore/macro.h>
40
41 #include "thread.h"
42
43 struct pa_thread {
44 pthread_t id;
45 pa_thread_func_t thread_func;
46 void *userdata;
47 pa_atomic_t running;
48 pa_bool_t joined;
49 char *name;
50 };
51
52 struct pa_tls {
53 pthread_key_t key;
54 };
55
56 static void thread_free_cb(void *p) {
57 pa_thread *t = p;
58
59 pa_assert(t);
60
61 if (!t->thread_func) {
62 /* This is a foreign thread, we need to free the struct */
63 pa_xfree(t->name);
64 pa_xfree(t);
65 }
66 }
67
68 PA_STATIC_TLS_DECLARE(current_thread, thread_free_cb);
69
70 static void* internal_thread_func(void *userdata) {
71 pa_thread *t = userdata;
72 pa_assert(t);
73
74 #ifdef __linux__
75 prctl(PR_SET_NAME, t->name);
76 #elif defined(HAVE_PTHREAD_SETNAME_NP) && defined(OS_IS_DARWIN)
77 pthread_setname_np(t->name);
78 #endif
79
80 t->id = pthread_self();
81
82 PA_STATIC_TLS_SET(current_thread, t);
83
84 pa_atomic_inc(&t->running);
85 t->thread_func(t->userdata);
86 pa_atomic_sub(&t->running, 2);
87
88 return NULL;
89 }
90
91 pa_thread* pa_thread_new(const char *name, pa_thread_func_t thread_func, void *userdata) {
92 pa_thread *t;
93
94 pa_assert(thread_func);
95
96 t = pa_xnew0(pa_thread, 1);
97 t->name = pa_xstrdup(name);
98 t->thread_func = thread_func;
99 t->userdata = userdata;
100
101 if (pthread_create(&t->id, NULL, internal_thread_func, t) < 0) {
102 pa_xfree(t);
103 return NULL;
104 }
105
106 pa_atomic_inc(&t->running);
107
108 return t;
109 }
110
111 int pa_thread_is_running(pa_thread *t) {
112 pa_assert(t);
113
114 /* Unfortunately there is no way to tell whether a "foreign"
115 * thread is still running. See
116 * http://udrepper.livejournal.com/16844.html for more
117 * information */
118 pa_assert(t->thread_func);
119
120 return pa_atomic_load(&t->running) > 0;
121 }
122
123 void pa_thread_free(pa_thread *t) {
124 pa_assert(t);
125
126 pa_thread_join(t);
127
128 pa_xfree(t->name);
129 pa_xfree(t);
130 }
131
132 int pa_thread_join(pa_thread *t) {
133 pa_assert(t);
134 pa_assert(t->thread_func);
135
136 if (t->joined)
137 return -1;
138
139 t->joined = TRUE;
140 return pthread_join(t->id, NULL);
141 }
142
143 pa_thread* pa_thread_self(void) {
144 pa_thread *t;
145
146 if ((t = PA_STATIC_TLS_GET(current_thread)))
147 return t;
148
149 /* This is a foreign thread, let's create a pthread structure to
150 * make sure that we can always return a sensible pointer */
151
152 t = pa_xnew0(pa_thread, 1);
153 t->id = pthread_self();
154 t->joined = TRUE;
155 pa_atomic_store(&t->running, 2);
156
157 PA_STATIC_TLS_SET(current_thread, t);
158
159 return t;
160 }
161
162 void* pa_thread_get_data(pa_thread *t) {
163 pa_assert(t);
164
165 return t->userdata;
166 }
167
168 void pa_thread_set_data(pa_thread *t, void *userdata) {
169 pa_assert(t);
170
171 t->userdata = userdata;
172 }
173
174 void pa_thread_set_name(pa_thread *t, const char *name) {
175 pa_assert(t);
176
177 pa_xfree(t->name);
178 t->name = pa_xstrdup(name);
179
180 #ifdef __linux__
181 prctl(PR_SET_NAME, name);
182 #elif defined(HAVE_PTHREAD_SETNAME_NP) && defined(OS_IS_DARWIN)
183 pthread_setname_np(name);
184 #endif
185 }
186
187 const char *pa_thread_get_name(pa_thread *t) {
188 pa_assert(t);
189
190 #ifdef __linux__
191 if (!t->name) {
192 t->name = pa_xmalloc(17);
193
194 if (prctl(PR_GET_NAME, t->name) >= 0)
195 t->name[16] = 0;
196 else {
197 pa_xfree(t->name);
198 t->name = NULL;
199 }
200 }
201 #elif defined(HAVE_PTHREAD_GETNAME_NP) && defined(OS_IS_DARWIN)
202 if (!t->name) {
203 t->name = pa_xmalloc0(17);
204 pthread_getname_np(t->id, t->name, 16);
205 }
206 #endif
207
208 return t->name;
209 }
210
211 void pa_thread_yield(void) {
212 #ifdef HAVE_PTHREAD_YIELD
213 pthread_yield();
214 #else
215 pa_assert_se(sched_yield() == 0);
216 #endif
217 }
218
219 pa_tls* pa_tls_new(pa_free_cb_t free_cb) {
220 pa_tls *t;
221
222 t = pa_xnew(pa_tls, 1);
223
224 if (pthread_key_create(&t->key, free_cb) < 0) {
225 pa_xfree(t);
226 return NULL;
227 }
228
229 return t;
230 }
231
232 void pa_tls_free(pa_tls *t) {
233 pa_assert(t);
234
235 pa_assert_se(pthread_key_delete(t->key) == 0);
236 pa_xfree(t);
237 }
238
239 void *pa_tls_get(pa_tls *t) {
240 pa_assert(t);
241
242 return pthread_getspecific(t->key);
243 }
244
245 void *pa_tls_set(pa_tls *t, void *userdata) {
246 void *r;
247
248 r = pthread_getspecific(t->key);
249 pa_assert_se(pthread_setspecific(t->key, userdata) == 0);
250 return r;
251 }