]> code.delx.au - pulseaudio/blob - src/pulsecore/thread-posix.c
once: make once related variables volatile
[pulseaudio] / 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_xnew0(pa_thread, 1);
84 t->thread_func = thread_func;
85 t->userdata = userdata;
86
87 if (pthread_create(&t->id, NULL, internal_thread_func, t) < 0) {
88 pa_xfree(t);
89 return NULL;
90 }
91
92 pa_atomic_inc(&t->running);
93
94 return t;
95 }
96
97 int pa_thread_is_running(pa_thread *t) {
98 pa_assert(t);
99
100 /* Unfortunately there is no way to tell whether a "foreign"
101 * thread is still running. See
102 * http://udrepper.livejournal.com/16844.html for more
103 * information */
104 pa_assert(t->thread_func);
105
106 return pa_atomic_load(&t->running) > 0;
107 }
108
109 void pa_thread_free(pa_thread *t) {
110 pa_assert(t);
111
112 pa_thread_join(t);
113 pa_xfree(t);
114 }
115
116 int pa_thread_join(pa_thread *t) {
117 pa_assert(t);
118 pa_assert(t->thread_func);
119
120 if (t->joined)
121 return -1;
122
123 t->joined = TRUE;
124 return pthread_join(t->id, NULL);
125 }
126
127 pa_thread* pa_thread_self(void) {
128 pa_thread *t;
129
130 if ((t = PA_STATIC_TLS_GET(current_thread)))
131 return t;
132
133 /* This is a foreign thread, let's create a pthread structure to
134 * make sure that we can always return a sensible pointer */
135
136 t = pa_xnew0(pa_thread, 1);
137 t->id = pthread_self();
138 t->joined = TRUE;
139 pa_atomic_store(&t->running, 2);
140
141 PA_STATIC_TLS_SET(current_thread, t);
142
143 return t;
144 }
145
146 void* pa_thread_get_data(pa_thread *t) {
147 pa_assert(t);
148
149 return t->userdata;
150 }
151
152 void pa_thread_set_data(pa_thread *t, void *userdata) {
153 pa_assert(t);
154
155 t->userdata = userdata;
156 }
157
158 void pa_thread_yield(void) {
159 #ifdef HAVE_PTHREAD_YIELD
160 pthread_yield();
161 #else
162 pa_assert_se(sched_yield() == 0);
163 #endif
164 }
165
166 pa_tls* pa_tls_new(pa_free_cb_t free_cb) {
167 pa_tls *t;
168
169 t = pa_xnew(pa_tls, 1);
170
171 if (pthread_key_create(&t->key, free_cb) < 0) {
172 pa_xfree(t);
173 return NULL;
174 }
175
176 return t;
177 }
178
179 void pa_tls_free(pa_tls *t) {
180 pa_assert(t);
181
182 pa_assert_se(pthread_key_delete(t->key) == 0);
183 pa_xfree(t);
184 }
185
186 void *pa_tls_get(pa_tls *t) {
187 pa_assert(t);
188
189 return pthread_getspecific(t->key);
190 }
191
192 void *pa_tls_set(pa_tls *t, void *userdata) {
193 void *r;
194
195 r = pthread_getspecific(t->key);
196 pa_assert_se(pthread_setspecific(t->key, userdata) == 0);
197 return r;
198 }