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