]> code.delx.au - pulseaudio/blob - src/pulsecore/thread-posix.c
update for newer APIs: replace direct usage of libatomic_ops by usage of our own...
[pulseaudio] / src / pulsecore / thread-posix.c
1 /* $Id$ */
2
3 /***
4 This file is part of PulseAudio.
5
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 or (at your option) any later version.
10
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <assert.h>
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
36 #include "thread.h"
37
38 #define ASSERT_SUCCESS(x) do { \
39 int _r = (x); \
40 assert(_r == 0); \
41 } while(0)
42
43 struct pa_thread {
44 pthread_t id;
45 pa_thread_func_t thread_func;
46 void *userdata;
47 pa_atomic_int_t running;
48 };
49
50 struct pa_tls {
51 pthread_key_t key;
52 };
53
54 static pa_tls *thread_tls;
55 static pa_once_t thread_tls_once = PA_ONCE_INIT;
56
57 static void tls_free_cb(void *p) {
58 pa_thread *t = p;
59
60 assert(t);
61
62 if (!t->thread_func)
63 /* This is a foreign thread, we need to free the struct */
64 pa_xfree(t);
65 }
66
67 static void thread_tls_once_func(void) {
68 thread_tls = pa_tls_new(tls_free_cb);
69 assert(thread_tls);
70 }
71
72 static void* internal_thread_func(void *userdata) {
73 pa_thread *t = userdata;
74 assert(t);
75
76 t->id = pthread_self();
77
78 pa_once(&thread_tls_once, thread_tls_once_func);
79
80 pa_tls_set(thread_tls, t);
81
82 pa_atomic_inc(&t->running);
83 t->thread_func(t->userdata);
84 pa_atomic_add(&t->running, -2);
85
86 return NULL;
87 }
88
89 pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata) {
90 pa_thread *t;
91
92 assert(thread_func);
93
94 t = pa_xnew(pa_thread, 1);
95 t->thread_func = thread_func;
96 t->userdata = userdata;
97 pa_atomic_store(&t->running, 0);
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 assert(t);
111
112 if (!t->thread_func) {
113 /* Mhmm, this is a foreign thread, t->running is not
114 * necessarily valid. We misuse pthread_getschedparam() to
115 * check if the thread is valid. This might not be portable. */
116
117 int policy;
118 struct sched_param param;
119
120 return pthread_getschedparam(t->id, &policy, &param) >= 0 || errno != ESRCH;
121 }
122
123 return pa_atomic_load(&t->running) > 0;
124 }
125
126 void pa_thread_free(pa_thread *t) {
127 assert(t);
128
129 pa_thread_join(t);
130 pa_xfree(t);
131 }
132
133 int pa_thread_join(pa_thread *t) {
134 assert(t);
135
136 return pthread_join(t->id, NULL);
137 }
138
139 pa_thread* pa_thread_self(void) {
140 pa_thread *t;
141
142 pa_once(&thread_tls_once, thread_tls_once_func);
143
144 if ((t = pa_tls_get(thread_tls)))
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_xnew(pa_thread, 1);
151 t->id = pthread_self();
152 t->thread_func = NULL;
153 t->userdata = NULL;
154 pa_atomic_store(&t->running, 2);
155
156 pa_tls_set(thread_tls, t);
157
158 return t;
159 }
160
161 void* pa_thread_get_data(pa_thread *t) {
162 assert(t);
163
164 return t->userdata;
165 }
166
167 void pa_thread_set_data(pa_thread *t, void *userdata) {
168 assert(t);
169
170 t->userdata = userdata;
171 }
172
173 void pa_thread_yield(void) {
174 #ifdef HAVE_PTHREAD_YIELD
175 pthread_yield();
176 #else
177 ASSERT_SUCCESS(sched_yield());
178 #endif
179 }
180
181 pa_tls* pa_tls_new(pa_free_cb_t free_cb) {
182 pa_tls *t;
183
184 t = pa_xnew(pa_tls, 1);
185
186 if (pthread_key_create(&t->key, free_cb) < 0) {
187 pa_xfree(t);
188 return NULL;
189 }
190
191 return t;
192 }
193
194 void pa_tls_free(pa_tls *t) {
195 assert(t);
196
197 ASSERT_SUCCESS(pthread_key_delete(t->key));
198 pa_xfree(t);
199 }
200
201 void *pa_tls_get(pa_tls *t) {
202 assert(t);
203
204 return pthread_getspecific(t->key);
205 }
206
207 void *pa_tls_set(pa_tls *t, void *userdata) {
208 void *r;
209
210 r = pthread_getspecific(t->key);
211 ASSERT_SUCCESS(pthread_setspecific(t->key, userdata));
212 return r;
213 }
214