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