build-sys: Use ax_check_flag macros from autoconf archive
[pulseaudio-mirror.git] / src / pulsecore / lock-autospawn.c
blob40aa5e927b1c750ebd6f2551761bd4bcfffe6d58
1 /***
2 This file is part of PulseAudio.
4 Copyright 2008 Lennart Poettering
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.
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.
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 ***/
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include <errno.h>
27 #include <string.h>
28 #include <signal.h>
30 #ifdef HAVE_PTHREAD
31 #include <pthread.h>
32 #endif
34 #include <pulse/gccmacro.h>
35 #include <pulse/i18n.h>
36 #include <pulse/xmalloc.h>
38 #include <pulsecore/poll.h>
39 #include <pulsecore/mutex.h>
40 #include <pulsecore/thread.h>
41 #include <pulsecore/core-util.h>
43 #include "lock-autospawn.h"
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. */
54 #define AUTOSPAWN_LOCK "autospawn.lock"
56 static pa_mutex *mutex;
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 };
64 static enum {
65 STATE_IDLE,
66 STATE_OWNING,
67 STATE_TAKEN,
68 STATE_FAILED
69 } state = STATE_IDLE;
71 static void destroy_mutex(void) PA_GCC_DESTRUCTOR;
73 static int ref(void) {
75 if (n_ref > 0) {
77 pa_assert(pipe_fd[0] >= 0);
78 pa_assert(pipe_fd[1] >= 0);
79 pa_assert(lock_fd_mutex);
81 n_ref++;
83 return 0;
86 pa_assert(!lock_fd_mutex);
87 pa_assert(state == STATE_IDLE);
88 pa_assert(lock_fd < 0);
89 pa_assert(!thread);
90 pa_assert(pipe_fd[0] < 0);
91 pa_assert(pipe_fd[1] < 0);
93 if (pa_pipe_cloexec(pipe_fd) < 0)
94 return -1;
96 pa_make_fd_nonblock(pipe_fd[1]);
97 pa_make_fd_nonblock(pipe_fd[0]);
99 lock_fd_mutex = pa_mutex_new(FALSE, FALSE);
101 n_ref = 1;
102 return 0;
105 static void unref(pa_bool_t after_fork) {
107 pa_assert(n_ref > 0);
108 pa_assert(pipe_fd[0] >= 0);
109 pa_assert(pipe_fd[1] >= 0);
110 pa_assert(lock_fd_mutex);
112 n_ref--;
114 if (n_ref > 0)
115 return;
117 if (thread) {
118 pa_thread_free(thread);
119 thread = NULL;
122 pa_mutex_lock(lock_fd_mutex);
124 pa_assert(state != STATE_TAKEN);
126 if (state == STATE_OWNING) {
128 pa_assert(lock_fd >= 0);
130 if (after_fork)
131 pa_close(lock_fd);
132 else {
133 char *lf;
135 if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
136 pa_log_warn(_("Cannot access autospawn lock."));
138 pa_unlock_lockfile(lf, lock_fd);
139 pa_xfree(lf);
143 lock_fd = -1;
144 state = STATE_IDLE;
146 pa_mutex_unlock(lock_fd_mutex);
148 pa_mutex_free(lock_fd_mutex);
149 lock_fd_mutex = NULL;
151 pa_close(pipe_fd[0]);
152 pa_close(pipe_fd[1]);
153 pipe_fd[0] = pipe_fd[1] = -1;
156 static void ping(void) {
157 ssize_t s;
159 pa_assert(pipe_fd[1] >= 0);
161 for (;;) {
162 char x = 'x';
164 if ((s = pa_write(pipe_fd[1], &x, 1, NULL)) == 1)
165 break;
167 pa_assert(s < 0);
169 if (errno == EAGAIN)
170 break;
172 pa_assert(errno == EINTR);
176 static void wait_for_ping(void) {
177 ssize_t s;
178 char x;
179 struct pollfd pfd;
180 int k;
182 pa_assert(pipe_fd[0] >= 0);
184 memset(&pfd, 0, sizeof(pfd));
185 pfd.fd = pipe_fd[0];
186 pfd.events = POLLIN;
188 if ((k = pa_poll(&pfd, 1, -1)) != 1) {
189 pa_assert(k < 0);
190 pa_assert(errno == EINTR);
191 } else if ((s = pa_read(pipe_fd[0], &x, 1, NULL)) != 1) {
192 pa_assert(s < 0);
193 pa_assert(errno == EAGAIN);
197 static void empty_pipe(void) {
198 char x[16];
199 ssize_t s;
201 pa_assert(pipe_fd[0] >= 0);
203 if ((s = pa_read(pipe_fd[0], &x, sizeof(x), NULL)) < 1) {
204 pa_assert(s < 0);
205 pa_assert(errno == EAGAIN);
209 static void thread_func(void *u) {
210 int fd;
211 char *lf;
213 #ifdef HAVE_PTHREAD
214 sigset_t fullset;
216 /* No signals in this thread please */
217 sigfillset(&fullset);
218 pthread_sigmask(SIG_BLOCK, &fullset, NULL);
219 #endif
221 if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
222 pa_log_warn(_("Cannot access autospawn lock."));
223 goto fail;
226 if ((fd = pa_lock_lockfile(lf)) < 0)
227 goto fail;
229 pa_mutex_lock(lock_fd_mutex);
230 pa_assert(state == STATE_IDLE);
231 lock_fd = fd;
232 state = STATE_OWNING;
233 pa_mutex_unlock(lock_fd_mutex);
235 goto finish;
237 fail:
238 pa_mutex_lock(lock_fd_mutex);
239 pa_assert(state == STATE_IDLE);
240 state = STATE_FAILED;
241 pa_mutex_unlock(lock_fd_mutex);
243 finish:
244 pa_xfree(lf);
246 ping();
249 static int start_thread(void) {
251 if (!thread)
252 if (!(thread = pa_thread_new("autospawn", thread_func, NULL)))
253 return -1;
255 return 0;
258 static void create_mutex(void) {
259 PA_ONCE_BEGIN {
260 mutex = pa_mutex_new(FALSE, FALSE);
261 } PA_ONCE_END;
264 static void destroy_mutex(void) {
265 if (mutex)
266 pa_mutex_free(mutex);
269 int pa_autospawn_lock_init(void) {
270 int ret = -1;
272 create_mutex();
273 pa_mutex_lock(mutex);
275 if (ref() < 0)
276 ret = -1;
277 else
278 ret = pipe_fd[0];
280 pa_mutex_unlock(mutex);
282 return ret;
285 int pa_autospawn_lock_acquire(pa_bool_t block) {
286 int ret = -1;
288 create_mutex();
289 pa_mutex_lock(mutex);
290 pa_assert(n_ref >= 1);
292 pa_mutex_lock(lock_fd_mutex);
294 for (;;) {
296 empty_pipe();
298 if (state == STATE_OWNING) {
299 state = STATE_TAKEN;
300 ret = 1;
301 break;
304 if (state == STATE_FAILED) {
305 ret = -1;
306 break;
309 if (state == STATE_IDLE)
310 if (start_thread() < 0)
311 break;
313 if (!block) {
314 ret = 0;
315 break;
318 pa_mutex_unlock(lock_fd_mutex);
319 pa_mutex_unlock(mutex);
321 wait_for_ping();
323 pa_mutex_lock(mutex);
324 pa_mutex_lock(lock_fd_mutex);
327 pa_mutex_unlock(lock_fd_mutex);
329 pa_mutex_unlock(mutex);
331 return ret;
334 void pa_autospawn_lock_release(void) {
336 create_mutex();
337 pa_mutex_lock(mutex);
338 pa_assert(n_ref >= 1);
340 pa_assert(state == STATE_TAKEN);
341 state = STATE_OWNING;
343 ping();
345 pa_mutex_unlock(mutex);
348 void pa_autospawn_lock_done(pa_bool_t after_fork) {
350 create_mutex();
351 pa_mutex_lock(mutex);
352 pa_assert(n_ref >= 1);
354 unref(after_fork);
356 pa_mutex_unlock(mutex);