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