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