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