• 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     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