• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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