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 #ifndef OS_IS_WIN32
80 sigset_t mask;
81 sigset_t prev_mask;
82 struct sigaction sa;
83
84 sigfillset(&mask);
85
86 /* If SIGSYS is currently unblocked and trapped then keep it unblocked. */
87 if (!pthread_sigmask(SIG_SETMASK, NULL, &prev_mask) &&
88 !sigismember(&prev_mask, SIGSYS) &&
89 !sigaction(SIGSYS, NULL, &sa)
90 && sa.sa_handler != SIG_DFL) {
91 sigdelset(&mask, SIGSYS);
92 }
93
94 /* Make sure that signals are delivered to the main thread.
95 * Use SIG_SETMASK because SIG_BLOCK does an union with current set.*/
96 pthread_sigmask(SIG_SETMASK, &mask, NULL);
97 #endif
98
99 pa_mutex_lock(m->mutex);
100
101 (void) pa_mainloop_run(m->real_mainloop, NULL);
102
103 pa_mutex_unlock(m->mutex);
104 }
105
pa_threaded_mainloop_new(void)106 pa_threaded_mainloop *pa_threaded_mainloop_new(void) {
107 pa_threaded_mainloop *m;
108
109 pa_init_i18n();
110
111 m = pa_xnew0(pa_threaded_mainloop, 1);
112
113 if (!(m->real_mainloop = pa_mainloop_new())) {
114 pa_xfree(m);
115 return NULL;
116 }
117
118 m->mutex = pa_mutex_new(true, true);
119 m->cond = pa_cond_new();
120 m->accept_cond = pa_cond_new();
121
122 pa_mainloop_set_poll_func(m->real_mainloop, poll_func, m->mutex);
123
124 return m;
125 }
126
pa_threaded_mainloop_free(pa_threaded_mainloop * m)127 void pa_threaded_mainloop_free(pa_threaded_mainloop* m) {
128 pa_assert(m);
129
130 /* Make sure that this function is not called from the helper thread */
131 pa_assert((m->thread && !pa_thread_is_running(m->thread)) || !in_worker(m));
132
133 pa_threaded_mainloop_stop(m);
134
135 if (m->thread)
136 pa_thread_free(m->thread);
137
138 pa_mainloop_free(m->real_mainloop);
139
140 pa_mutex_free(m->mutex);
141 pa_cond_free(m->cond);
142 pa_cond_free(m->accept_cond);
143
144 pa_xfree(m->name);
145 pa_xfree(m);
146 }
147
pa_threaded_mainloop_start(pa_threaded_mainloop * m)148 int pa_threaded_mainloop_start(pa_threaded_mainloop *m) {
149 pa_assert(m);
150
151 pa_assert(!m->thread || !pa_thread_is_running(m->thread));
152
153 if (!(m->thread = pa_thread_new(m->name ? m->name : "threaded-ml", thread, m)))
154 return -1;
155
156 return 0;
157 }
158
pa_threaded_mainloop_stop(pa_threaded_mainloop * m)159 void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) {
160 pa_assert(m);
161
162 if (!m->thread || !pa_thread_is_running(m->thread))
163 return;
164
165 /* Make sure that this function is not called from the helper thread */
166 pa_assert(!in_worker(m));
167
168 pa_mutex_lock(m->mutex);
169 pa_mainloop_quit(m->real_mainloop, 0);
170 pa_mutex_unlock(m->mutex);
171
172 pa_thread_join(m->thread);
173 }
174
pa_threaded_mainloop_lock(pa_threaded_mainloop * m)175 void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) {
176 pa_assert(m);
177
178 /* Make sure that this function is not called from the helper thread, unless it is unlocked */
179 pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m) || pa_atomic_load(&m->in_once_unlocked));
180
181 pa_mutex_lock(m->mutex);
182 }
183
pa_threaded_mainloop_unlock(pa_threaded_mainloop * m)184 void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) {
185 pa_assert(m);
186
187 /* Make sure that this function is not called from the helper thread, unless it is unlocked */
188 pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m) || pa_atomic_load(&m->in_once_unlocked));
189
190 pa_mutex_unlock(m->mutex);
191 }
192
193 /* Called with the lock taken */
pa_threaded_mainloop_signal(pa_threaded_mainloop * m,int wait_for_accept)194 void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) {
195 pa_assert(m);
196
197 pa_cond_signal(m->cond, 1);
198
199 if (wait_for_accept) {
200 m->n_waiting_for_accept ++;
201
202 while (m->n_waiting_for_accept > 0)
203 pa_cond_wait(m->accept_cond, m->mutex);
204 }
205 }
206
207 /* Called with the lock taken */
pa_threaded_mainloop_wait(pa_threaded_mainloop * m)208 void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) {
209 pa_assert(m);
210
211 /* Make sure that this function is not called from the helper thread */
212 pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
213
214 m->n_waiting ++;
215
216 pa_cond_wait(m->cond, m->mutex);
217
218 pa_assert(m->n_waiting > 0);
219 m->n_waiting --;
220 }
221
222 /* Called with the lock taken */
pa_threaded_mainloop_accept(pa_threaded_mainloop * m)223 void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) {
224 pa_assert(m);
225
226 /* Make sure that this function is not called from the helper thread */
227 pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
228
229 pa_assert(m->n_waiting_for_accept > 0);
230 m->n_waiting_for_accept --;
231
232 pa_cond_signal(m->accept_cond, 0);
233 }
234
pa_threaded_mainloop_get_retval(const pa_threaded_mainloop * m)235 int pa_threaded_mainloop_get_retval(const pa_threaded_mainloop *m) {
236 pa_assert(m);
237
238 return pa_mainloop_get_retval(m->real_mainloop);
239 }
240
pa_threaded_mainloop_get_api(pa_threaded_mainloop * m)241 pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) {
242 pa_assert(m);
243
244 return pa_mainloop_get_api(m->real_mainloop);
245 }
246
pa_threaded_mainloop_in_thread(pa_threaded_mainloop * m)247 int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m) {
248 pa_assert(m);
249
250 return m->thread && pa_thread_self() == m->thread;
251 }
252
pa_threaded_mainloop_set_name(pa_threaded_mainloop * m,const char * name)253 void pa_threaded_mainloop_set_name(pa_threaded_mainloop *m, const char *name) {
254 pa_assert(m);
255 pa_assert(name);
256
257 m->name = pa_xstrdup(name);
258
259 if (m->thread)
260 pa_thread_set_name(m->thread, m->name);
261 }
262
263 typedef struct {
264 pa_threaded_mainloop *mainloop;
265 void (*callback)(pa_threaded_mainloop *m, void *userdata);
266 void *userdata;
267 } once_unlocked_data;
268
once_unlocked_cb(pa_mainloop_api * api,void * userdata)269 static void once_unlocked_cb(pa_mainloop_api *api, void *userdata) {
270 once_unlocked_data *data = userdata;
271
272 pa_assert(userdata);
273
274 pa_atomic_store(&data->mainloop->in_once_unlocked, 1);
275 pa_mutex_unlock(data->mainloop->mutex);
276
277 data->callback(data->mainloop, data->userdata);
278
279 pa_mutex_lock(data->mainloop->mutex);
280 pa_atomic_store(&data->mainloop->in_once_unlocked, 0);
281 }
282
pa_threaded_mainloop_once_unlocked(pa_threaded_mainloop * m,void (* callback)(pa_threaded_mainloop * m,void * userdata),void * userdata)283 void pa_threaded_mainloop_once_unlocked(pa_threaded_mainloop *m, void (*callback)(pa_threaded_mainloop *m, void *userdata),
284 void *userdata) {
285 pa_mainloop_api *api;
286 once_unlocked_data *data;
287
288 pa_assert(m);
289 pa_assert(callback);
290 /* Make sure that this function is not called from the helper thread */
291 pa_assert((m->thread && !pa_thread_is_running(m->thread)) || !in_worker(m));
292
293 api = pa_mainloop_get_api(m->real_mainloop);
294 data = pa_xnew0(once_unlocked_data, 1);
295
296 data->mainloop = m;
297 data->callback = callback;
298 data->userdata = userdata;
299
300 pa_mainloop_api_once(api, once_unlocked_cb, data);
301 }
302