• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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