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