Branch data Line data Source code
1 : : /***
2 : : This file is part of PulseAudio.
3 : :
4 : : Copyright 2008 Lennart Poettering
5 : :
6 : : PulseAudio is free software; you can redistribute it and/or modify
7 : : it under the terms of the GNU Lesser General Public License as published
8 : : by the Free Software Foundation; either version 2.1 of the License,
9 : : or (at your option) any later version.
10 : :
11 : : PulseAudio is distributed in the hope that it will be useful, but
12 : : WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : General Public License for more details.
15 : :
16 : : You should have received a copy of the GNU Lesser General Public License
17 : : along with PulseAudio; if not, write to the Free Software
18 : : Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 : : USA.
20 : : ***/
21 : :
22 : : #ifdef HAVE_CONFIG_H
23 : : #include <config.h>
24 : : #endif
25 : :
26 : : #include <errno.h>
27 : : #include <string.h>
28 : : #include <signal.h>
29 : :
30 : : #ifdef HAVE_PTHREAD
31 : : #include <pthread.h>
32 : : #endif
33 : :
34 : : #include <pulse/gccmacro.h>
35 : : #include <pulse/xmalloc.h>
36 : :
37 : : #include <pulsecore/i18n.h>
38 : : #include <pulsecore/poll.h>
39 : : #include <pulsecore/mutex.h>
40 : : #include <pulsecore/thread.h>
41 : : #include <pulsecore/core-util.h>
42 : :
43 : : #include "lock-autospawn.h"
44 : :
45 : : /* So, why do we have this complex code here with threads and pipes
46 : : * and stuff? For two reasons: POSIX file locks are per-process, not
47 : : * per-file descriptor. That means that two contexts within the same
48 : : * process that try to create the autospawn lock might end up assuming
49 : : * they both managed to lock the file. And then, POSIX locking
50 : : * operations are synchronous. If two contexts run from the same event
51 : : * loop it must be made sure that they do not block each other, but
52 : : * that the locking operation can happen asynchronously. */
53 : :
54 : : #define AUTOSPAWN_LOCK "autospawn.lock"
55 : :
56 : : static pa_mutex *mutex;
57 : :
58 : : static unsigned n_ref = 0;
59 : : static int lock_fd = -1;
60 : : static pa_mutex *lock_fd_mutex = NULL;
61 : : static pa_thread *thread = NULL;
62 : : static int pipe_fd[2] = { -1, -1 };
63 : :
64 : : static enum {
65 : : STATE_IDLE,
66 : : STATE_OWNING,
67 : : STATE_TAKEN,
68 : : STATE_FAILED
69 : : } state = STATE_IDLE;
70 : :
71 : : static void destroy_mutex(void) PA_GCC_DESTRUCTOR;
72 : :
73 : 4 : static int ref(void) {
74 : :
75 [ + + ]: 4 : if (n_ref > 0) {
76 : :
77 [ - + ]: 3 : pa_assert(pipe_fd[0] >= 0);
78 [ - + ]: 3 : pa_assert(pipe_fd[1] >= 0);
79 [ - + ]: 3 : pa_assert(lock_fd_mutex);
80 : :
81 : 3 : n_ref++;
82 : :
83 : 3 : return 0;
84 : : }
85 : :
86 [ - + ]: 1 : pa_assert(!lock_fd_mutex);
87 [ - + ]: 1 : pa_assert(state == STATE_IDLE);
88 [ - + ]: 1 : pa_assert(lock_fd < 0);
89 [ - + ]: 1 : pa_assert(!thread);
90 [ - + ]: 1 : pa_assert(pipe_fd[0] < 0);
91 [ - + ]: 1 : pa_assert(pipe_fd[1] < 0);
92 : :
93 [ + - ]: 1 : if (pa_pipe_cloexec(pipe_fd) < 0)
94 : : return -1;
95 : :
96 : 1 : pa_make_fd_nonblock(pipe_fd[1]);
97 : 1 : pa_make_fd_nonblock(pipe_fd[0]);
98 : :
99 : 1 : lock_fd_mutex = pa_mutex_new(FALSE, FALSE);
100 : :
101 : 1 : n_ref = 1;
102 : 4 : return 0;
103 : : }
104 : :
105 : 4 : static void unref(pa_bool_t after_fork) {
106 : :
107 [ - + ]: 4 : pa_assert(n_ref > 0);
108 [ - + ]: 4 : pa_assert(pipe_fd[0] >= 0);
109 [ - + ]: 4 : pa_assert(pipe_fd[1] >= 0);
110 [ - + ]: 4 : pa_assert(lock_fd_mutex);
111 : :
112 : 4 : n_ref--;
113 : :
114 [ + + ]: 4 : if (n_ref > 0)
115 : 4 : return;
116 : :
117 [ + - ]: 1 : if (thread) {
118 : 1 : pa_thread_free(thread);
119 : 1 : thread = NULL;
120 : : }
121 : :
122 : 1 : pa_mutex_lock(lock_fd_mutex);
123 : :
124 [ - + ]: 1 : pa_assert(state != STATE_TAKEN);
125 : :
126 [ + - ]: 1 : if (state == STATE_OWNING) {
127 : :
128 [ - + ]: 1 : pa_assert(lock_fd >= 0);
129 : :
130 [ - + ]: 1 : if (after_fork)
131 : 0 : pa_close(lock_fd);
132 : : else {
133 : : char *lf;
134 : :
135 [ - + ]: 1 : if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
136 : 0 : pa_log_warn(_("Cannot access autospawn lock."));
137 : :
138 : 1 : pa_unlock_lockfile(lf, lock_fd);
139 : 1 : pa_xfree(lf);
140 : : }
141 : : }
142 : :
143 : 1 : lock_fd = -1;
144 : 1 : state = STATE_IDLE;
145 : :
146 : 1 : pa_mutex_unlock(lock_fd_mutex);
147 : :
148 : 1 : pa_mutex_free(lock_fd_mutex);
149 : 1 : lock_fd_mutex = NULL;
150 : :
151 : 1 : pa_close(pipe_fd[0]);
152 : 1 : pa_close(pipe_fd[1]);
153 : 1 : pipe_fd[0] = pipe_fd[1] = -1;
154 : : }
155 : :
156 : 5 : static void ping(void) {
157 : : ssize_t s;
158 : :
159 [ - + ]: 5 : pa_assert(pipe_fd[1] >= 0);
160 : :
161 : : for (;;) {
162 : 5 : char x = 'x';
163 : :
164 [ - + ]: 5 : if ((s = pa_write(pipe_fd[1], &x, 1, NULL)) == 1)
165 : : break;
166 : :
167 [ # # ]: 0 : pa_assert(s < 0);
168 : :
169 [ # # ]: 0 : if (errno == EAGAIN)
170 : : break;
171 : :
172 [ # # ]: 0 : pa_assert(errno == EINTR);
173 : : }
174 : 5 : }
175 : :
176 : 3 : static void wait_for_ping(void) {
177 : : ssize_t s;
178 : : char x;
179 : : struct pollfd pfd;
180 : : int k;
181 : :
182 [ - + ]: 3 : pa_assert(pipe_fd[0] >= 0);
183 : :
184 : : memset(&pfd, 0, sizeof(pfd));
185 : 3 : pfd.fd = pipe_fd[0];
186 : 3 : pfd.events = POLLIN;
187 : :
188 [ - + ]: 3 : if ((k = pa_poll(&pfd, 1, -1)) != 1) {
189 [ # # ]: 0 : pa_assert(k < 0);
190 [ # # ]: 0 : pa_assert(errno == EINTR);
191 [ + + ]: 3 : } else if ((s = pa_read(pipe_fd[0], &x, 1, NULL)) != 1) {
192 [ - + ]: 1 : pa_assert(s < 0);
193 [ - + ]: 1 : pa_assert(errno == EAGAIN);
194 : : }
195 : 3 : }
196 : :
197 : 13 : static void empty_pipe(void) {
198 : : char x[16];
199 : : ssize_t s;
200 : :
201 [ - + ]: 13 : pa_assert(pipe_fd[0] >= 0);
202 : :
203 [ + + ]: 13 : if ((s = pa_read(pipe_fd[0], &x, sizeof(x), NULL)) < 1) {
204 [ - + ]: 11 : pa_assert(s < 0);
205 [ - + ]: 11 : pa_assert(errno == EAGAIN);
206 : : }
207 : 13 : }
208 : :
209 : 1 : static void thread_func(void *u) {
210 : : int fd;
211 : : char *lf;
212 : :
213 : : #ifdef HAVE_PTHREAD
214 : : sigset_t fullset;
215 : :
216 : : /* No signals in this thread please */
217 : 1 : sigfillset(&fullset);
218 : 1 : pthread_sigmask(SIG_BLOCK, &fullset, NULL);
219 : : #endif
220 : :
221 [ - + ]: 1 : if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
222 : 0 : pa_log_warn(_("Cannot access autospawn lock."));
223 : 0 : goto fail;
224 : : }
225 : :
226 [ + - ]: 1 : if ((fd = pa_lock_lockfile(lf)) < 0)
227 : : goto fail;
228 : :
229 : 1 : pa_mutex_lock(lock_fd_mutex);
230 [ - + ]: 1 : pa_assert(state == STATE_IDLE);
231 : 1 : lock_fd = fd;
232 : 1 : state = STATE_OWNING;
233 : 1 : pa_mutex_unlock(lock_fd_mutex);
234 : :
235 : 1 : goto finish;
236 : :
237 : : fail:
238 : 0 : pa_mutex_lock(lock_fd_mutex);
239 [ # # ]: 0 : pa_assert(state == STATE_IDLE);
240 : 0 : state = STATE_FAILED;
241 : 0 : pa_mutex_unlock(lock_fd_mutex);
242 : :
243 : : finish:
244 : 1 : pa_xfree(lf);
245 : :
246 : 1 : ping();
247 : 1 : }
248 : :
249 : : static int start_thread(void) {
250 : :
251 [ + + ]: 4 : if (!thread)
252 [ + - ]: 1 : if (!(thread = pa_thread_new("autospawn", thread_func, NULL)))
253 : : return -1;
254 : :
255 : : return 0;
256 : : }
257 : :
258 : 22 : static void create_mutex(void) {
259 [ + + ]: 22 : PA_ONCE_BEGIN {
260 : 1 : mutex = pa_mutex_new(FALSE, FALSE);
261 : 1 : } PA_ONCE_END;
262 : 22 : }
263 : :
264 : 27 : static void destroy_mutex(void) {
265 [ + + ]: 27 : if (mutex)
266 : 1 : pa_mutex_free(mutex);
267 : 27 : }
268 : :
269 : 4 : int pa_autospawn_lock_init(void) {
270 : 4 : int ret = -1;
271 : :
272 : 4 : create_mutex();
273 : 4 : pa_mutex_lock(mutex);
274 : :
275 [ + - ]: 4 : if (ref() < 0)
276 : : ret = -1;
277 : : else
278 : 4 : ret = pipe_fd[0];
279 : :
280 : 4 : pa_mutex_unlock(mutex);
281 : :
282 : 4 : return ret;
283 : : }
284 : :
285 : 10 : int pa_autospawn_lock_acquire(pa_bool_t block) {
286 : 10 : int ret = -1;
287 : :
288 : 10 : create_mutex();
289 : 10 : pa_mutex_lock(mutex);
290 [ - + ]: 10 : pa_assert(n_ref >= 1);
291 : :
292 : 10 : pa_mutex_lock(lock_fd_mutex);
293 : :
294 : : for (;;) {
295 : :
296 : 13 : empty_pipe();
297 : :
298 [ + + ]: 13 : if (state == STATE_OWNING) {
299 : 4 : state = STATE_TAKEN;
300 : 4 : ret = 1;
301 : 4 : break;
302 : : }
303 : :
304 [ + - ]: 9 : if (state == STATE_FAILED) {
305 : : ret = -1;
306 : : break;
307 : : }
308 : :
309 [ + + ]: 9 : if (state == STATE_IDLE)
310 [ + - ]: 4 : if (start_thread() < 0)
311 : : break;
312 : :
313 [ + + ]: 9 : if (!block) {
314 : : ret = 0;
315 : : break;
316 : : }
317 : :
318 : 3 : pa_mutex_unlock(lock_fd_mutex);
319 : 3 : pa_mutex_unlock(mutex);
320 : :
321 : 3 : wait_for_ping();
322 : :
323 : 3 : pa_mutex_lock(mutex);
324 : 3 : pa_mutex_lock(lock_fd_mutex);
325 : 3 : }
326 : :
327 : 10 : pa_mutex_unlock(lock_fd_mutex);
328 : :
329 : 10 : pa_mutex_unlock(mutex);
330 : :
331 : 10 : return ret;
332 : : }
333 : :
334 : 4 : void pa_autospawn_lock_release(void) {
335 : :
336 : 4 : create_mutex();
337 : 4 : pa_mutex_lock(mutex);
338 [ - + ]: 4 : pa_assert(n_ref >= 1);
339 : :
340 [ - + ]: 4 : pa_assert(state == STATE_TAKEN);
341 : 4 : state = STATE_OWNING;
342 : :
343 : 4 : ping();
344 : :
345 : 4 : pa_mutex_unlock(mutex);
346 : 4 : }
347 : :
348 : 4 : void pa_autospawn_lock_done(pa_bool_t after_fork) {
349 : :
350 : 4 : create_mutex();
351 : 4 : pa_mutex_lock(mutex);
352 [ - + ]: 4 : pa_assert(n_ref >= 1);
353 : :
354 : 4 : unref(after_fork);
355 : :
356 : 4 : pa_mutex_unlock(mutex);
357 : 4 : }
|