Git init
[framework/multimedia/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 #include <pulse/xmalloc.h>
32 #include <pulsecore/mutex.h>
33 #include <pulsecore/once.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     pa_bool_t joined;
45 };
46
47 struct pa_tls {
48     pthread_key_t key;
49 };
50
51 static void thread_free_cb(void *p) {
52     pa_thread *t = p;
53
54     pa_assert(t);
55
56     if (!t->thread_func)
57         /* This is a foreign thread, we need to free the struct */
58         pa_xfree(t);
59 }
60
61 PA_STATIC_TLS_DECLARE(current_thread, thread_free_cb);
62
63 static void* internal_thread_func(void *userdata) {
64     pa_thread *t = userdata;
65     pa_assert(t);
66
67     t->id = pthread_self();
68
69     PA_STATIC_TLS_SET(current_thread, t);
70
71     pa_atomic_inc(&t->running);
72     t->thread_func(t->userdata);
73     pa_atomic_sub(&t->running, 2);
74
75     return NULL;
76 }
77
78 pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata) {
79     pa_thread *t;
80
81     pa_assert(thread_func);
82
83     t = pa_xnew(pa_thread, 1);
84     t->thread_func = thread_func;
85     t->userdata = userdata;
86     t->joined = FALSE;
87     pa_atomic_store(&t->running, 0);
88
89     if (pthread_create(&t->id, NULL, internal_thread_func, t) < 0) {
90         pa_xfree(t);
91         return NULL;
92     }
93
94     pa_atomic_inc(&t->running);
95
96     return t;
97 }
98
99 int pa_thread_is_running(pa_thread *t) {
100     pa_assert(t);
101
102     /* Unfortunately there is no way to tell whether a "foreign"
103      * thread is still running. See
104      * http://udrepper.livejournal.com/16844.html for more
105      * information */
106     pa_assert(t->thread_func);
107
108     return pa_atomic_load(&t->running) > 0;
109 }
110
111 void pa_thread_free(pa_thread *t) {
112     pa_assert(t);
113
114     pa_thread_join(t);
115     pa_xfree(t);
116 }
117
118 int pa_thread_join(pa_thread *t) {
119     pa_assert(t);
120     pa_assert(t->thread_func);
121
122     if (t->joined)
123         return -1;
124
125     t->joined = TRUE;
126     return pthread_join(t->id, NULL);
127 }
128
129 pa_thread* pa_thread_self(void) {
130     pa_thread *t;
131
132     if ((t = PA_STATIC_TLS_GET(current_thread)))
133         return t;
134
135     /* This is a foreign thread, let's create a pthread structure to
136      * make sure that we can always return a sensible pointer */
137
138     t = pa_xnew(pa_thread, 1);
139     t->id = pthread_self();
140     t->thread_func = NULL;
141     t->userdata = NULL;
142     t->joined = TRUE;
143     pa_atomic_store(&t->running, 2);
144
145     PA_STATIC_TLS_SET(current_thread, t);
146
147     return t;
148 }
149
150 void* pa_thread_get_data(pa_thread *t) {
151     pa_assert(t);
152
153     return t->userdata;
154 }
155
156 void pa_thread_set_data(pa_thread *t, void *userdata) {
157     pa_assert(t);
158
159     t->userdata = userdata;
160 }
161
162 void pa_thread_yield(void) {
163 #ifdef HAVE_PTHREAD_YIELD
164     pthread_yield();
165 #else
166     pa_assert_se(sched_yield() == 0);
167 #endif
168 }
169
170 pa_tls* pa_tls_new(pa_free_cb_t free_cb) {
171     pa_tls *t;
172
173     t = pa_xnew(pa_tls, 1);
174
175     if (pthread_key_create(&t->key, free_cb) < 0) {
176         pa_xfree(t);
177         return NULL;
178     }
179
180     return t;
181 }
182
183 void pa_tls_free(pa_tls *t) {
184     pa_assert(t);
185
186     pa_assert_se(pthread_key_delete(t->key) == 0);
187     pa_xfree(t);
188 }
189
190 void *pa_tls_get(pa_tls *t) {
191     pa_assert(t);
192
193     return pthread_getspecific(t->key);
194 }
195
196 void *pa_tls_set(pa_tls *t, void *userdata) {
197     void *r;
198
199     r = pthread_getspecific(t->key);
200     pa_assert_se(pthread_setspecific(t->key, userdata) == 0);
201     return r;
202 }