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 #ifndef OS_IS_WIN32
26 #include <pthread.h>
27 #endif
28
29 #include <signal.h>
30 #include <stdio.h>
31
32 #include <pulse/xmalloc.h>
33 #include <pulse/mainloop.h>
34
35 #include <pulsecore/i18n.h>
36 #include <pulsecore/log.h>
37 #include <pulsecore/thread.h>
38 #include <pulsecore/mutex.h>
39 #include <pulsecore/macro.h>
40 #include <pulsecore/poll.h>
41
42 #include "thread-mainloop.h"
43
44 struct pa_threaded_mainloop {
45 pa_mainloop *real_mainloop;
46 volatile int n_waiting, n_waiting_for_accept;
47 pa_atomic_t in_once_unlocked;
48
49 pa_thread* thread;
50 pa_mutex* mutex;
51 pa_cond* cond, *accept_cond;
52
53 char *name;
54 };
55
in_worker(pa_threaded_mainloop * m)56 static inline int in_worker(pa_threaded_mainloop *m) {
57 return pa_thread_self() == m->thread;
58 }
59
poll_func(struct pollfd * ufds,unsigned long nfds,int timeout,void * userdata)60 static int poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) {
61 pa_mutex *mutex = userdata;
62 int r;
63
64 pa_assert(mutex);
65
66 /* Before entering poll() we unlock the mutex, so that
67 * avahi_simple_poll_quit() can succeed from another thread. */
68
69 pa_mutex_unlock(mutex);
70 r = pa_poll(ufds, nfds, timeout);
71 pa_mutex_lock(mutex);
72
73 return r;
74 }
75
thread(void * userdata)76 static void thread(void *userdata) {
77 pa_threaded_mainloop *m = userdata;
78
79 pa_mutex_lock(m->mutex);
80
81 (void) pa_mainloop_run(m->real_mainloop, NULL);
82
83 pa_mutex_unlock(m->mutex);
84 }
85
pa_threaded_mainloop_new(void)86 pa_threaded_mainloop *pa_threaded_mainloop_new(void) {
87 pa_threaded_mainloop *m;
88
89 pa_init_i18n();
90
91 m = pa_xnew0(pa_threaded_mainloop, 1);
92
93 if (!(m->real_mainloop = pa_mainloop_new())) {
94 pa_xfree(m);
95 return NULL;
96 }
97
98 m->mutex = pa_mutex_new(true, true);
99 m->cond = pa_cond_new();
100 m->accept_cond = pa_cond_new();
101
102 pa_mainloop_set_poll_func(m->real_mainloop, poll_func, m->mutex);
103
104 return m;
105 }
106
pa_threaded_mainloop_free(pa_threaded_mainloop * m)107 void pa_threaded_mainloop_free(pa_threaded_mainloop* m) {
108 pa_assert(m);
109
110 /* Make sure that this function is not called from the helper thread */
111 pa_assert((m->thread && !pa_thread_is_running(m->thread)) || !in_worker(m));
112
113 pa_threaded_mainloop_stop(m);
114
115 if (m->thread)
116 pa_thread_free(m->thread);
117
118 pa_mainloop_free(m->real_mainloop);
119
120 pa_mutex_free(m->mutex);
121 pa_cond_free(m->cond);
122 pa_cond_free(m->accept_cond);
123
124 pa_xfree(m->name);
125 pa_xfree(m);
126 }
127
pa_threaded_mainloop_start(pa_threaded_mainloop * m)128 int pa_threaded_mainloop_start(pa_threaded_mainloop *m) {
129 pa_assert(m);
130
131 pa_assert(!m->thread || !pa_thread_is_running(m->thread));
132
133 if (!(m->thread = pa_thread_new(m->name ? m->name : "threaded-ml", thread, m)))
134 return -1;
135
136 return 0;
137 }
138
pa_threaded_mainloop_stop(pa_threaded_mainloop * m)139 void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) {
140 pa_assert(m);
141
142 if (!m->thread || !pa_thread_is_running(m->thread))
143 return;
144
145 /* Make sure that this function is not called from the helper thread */
146 pa_assert(!in_worker(m));
147
148 pa_mutex_lock(m->mutex);
149 pa_mainloop_quit(m->real_mainloop, 0);
150 pa_mutex_unlock(m->mutex);
151
152 pa_thread_join(m->thread);
153 }
154
pa_threaded_mainloop_lock(pa_threaded_mainloop * m)155 void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) {
156 pa_assert(m);
157
158 /* Make sure that this function is not called from the helper thread, unless it is unlocked */
159 pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m) || pa_atomic_load(&m->in_once_unlocked));
160
161 pa_mutex_lock(m->mutex);
162 }
163
pa_threaded_mainloop_unlock(pa_threaded_mainloop * m)164 void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) {
165 pa_assert(m);
166
167 /* Make sure that this function is not called from the helper thread, unless it is unlocked */
168 pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m) || pa_atomic_load(&m->in_once_unlocked));
169
170 pa_mutex_unlock(m->mutex);
171 }
172
173 /* Called with the lock taken */
pa_threaded_mainloop_signal(pa_threaded_mainloop * m,int wait_for_accept)174 void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) {
175 pa_assert(m);
176
177 pa_cond_signal(m->cond, 1);
178
179 if (wait_for_accept) {
180 m->n_waiting_for_accept ++;
181
182 while (m->n_waiting_for_accept > 0)
183 pa_cond_wait(m->accept_cond, m->mutex);
184 }
185 }
186
187 /* Called with the lock taken */
pa_threaded_mainloop_wait(pa_threaded_mainloop * m)188 void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) {
189 pa_assert(m);
190
191 /* Make sure that this function is not called from the helper thread */
192 pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
193
194 m->n_waiting ++;
195
196 pa_cond_wait(m->cond, m->mutex);
197
198 pa_assert(m->n_waiting > 0);
199 m->n_waiting --;
200 }
201
202 /* Called with the lock taken */
pa_threaded_mainloop_accept(pa_threaded_mainloop * m)203 void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) {
204 pa_assert(m);
205
206 /* Make sure that this function is not called from the helper thread */
207 pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
208
209 pa_assert(m->n_waiting_for_accept > 0);
210 m->n_waiting_for_accept --;
211
212 pa_cond_signal(m->accept_cond, 0);
213 }
214
pa_threaded_mainloop_get_retval(const pa_threaded_mainloop * m)215 int pa_threaded_mainloop_get_retval(const pa_threaded_mainloop *m) {
216 pa_assert(m);
217
218 return pa_mainloop_get_retval(m->real_mainloop);
219 }
220
pa_threaded_mainloop_get_api(pa_threaded_mainloop * m)221 pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) {
222 pa_assert(m);
223
224 return pa_mainloop_get_api(m->real_mainloop);
225 }
226
pa_threaded_mainloop_in_thread(pa_threaded_mainloop * m)227 int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m) {
228 pa_assert(m);
229
230 return m->thread && pa_thread_self() == m->thread;
231 }
232
pa_threaded_mainloop_set_name(pa_threaded_mainloop * m,const char * name)233 void pa_threaded_mainloop_set_name(pa_threaded_mainloop *m, const char *name) {
234 pa_assert(m);
235 pa_assert(name);
236
237 m->name = pa_xstrdup(name);
238
239 if (m->thread)
240 pa_thread_set_name(m->thread, m->name);
241 }
242
243 typedef struct {
244 pa_threaded_mainloop *mainloop;
245 void (*callback)(pa_threaded_mainloop *m, void *userdata);
246 void *userdata;
247 } once_unlocked_data;
248
once_unlocked_cb(pa_mainloop_api * api,void * userdata)249 static void once_unlocked_cb(pa_mainloop_api *api, void *userdata) {
250 once_unlocked_data *data = userdata;
251
252 pa_assert(userdata);
253
254 pa_atomic_store(&data->mainloop->in_once_unlocked, 1);
255 pa_mutex_unlock(data->mainloop->mutex);
256
257 data->callback(data->mainloop, data->userdata);
258
259 pa_mutex_lock(data->mainloop->mutex);
260 pa_atomic_store(&data->mainloop->in_once_unlocked, 0);
261
262 pa_xfree(data);
263 }
264
pa_threaded_mainloop_once_unlocked(pa_threaded_mainloop * m,void (* callback)(pa_threaded_mainloop * m,void * userdata),void * userdata)265 void pa_threaded_mainloop_once_unlocked(pa_threaded_mainloop *m, void (*callback)(pa_threaded_mainloop *m, void *userdata),
266 void *userdata) {
267 pa_mainloop_api *api;
268 once_unlocked_data *data;
269
270 pa_assert(m);
271 pa_assert(callback);
272 /* Make sure that this function is not called from the helper thread */
273 pa_assert((m->thread && !pa_thread_is_running(m->thread)) || !in_worker(m));
274
275 api = pa_mainloop_get_api(m->real_mainloop);
276 data = pa_xnew0(once_unlocked_data, 1);
277
278 data->mainloop = m;
279 data->callback = callback;
280 data->userdata = userdata;
281
282 pa_mainloop_api_once(api, once_unlocked_cb, data);
283 }
284