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