72806f8312476ca87b80005654bb11abf599da46
[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 <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 static int ref(void) {
74
75     if (n_ref > 0) {
76
77         pa_assert(pipe_fd[0] >= 0);
78         pa_assert(pipe_fd[1] >= 0);
79         pa_assert(lock_fd_mutex);
80
81         n_ref++;
82
83         return 0;
84     }
85
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);
92
93     if (pa_pipe_cloexec(pipe_fd) < 0)
94         return -1;
95
96     pa_make_fd_nonblock(pipe_fd[1]);
97     pa_make_fd_nonblock(pipe_fd[0]);
98
99     lock_fd_mutex = pa_mutex_new(false, false);
100
101     n_ref = 1;
102     return 0;
103 }
104
105 static void unref(bool after_fork) {
106
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);
111
112     n_ref--;
113
114     if (n_ref > 0)
115         return;
116
117     if (thread) {
118         pa_thread_free(thread);
119         thread = NULL;
120     }
121
122     pa_mutex_lock(lock_fd_mutex);
123
124     pa_assert(state != STATE_TAKEN);
125
126     if (state == STATE_OWNING) {
127
128         pa_assert(lock_fd >= 0);
129
130         if (after_fork)
131             pa_close(lock_fd);
132         else {
133             char *lf;
134
135             if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
136                 pa_log_warn(_("Cannot access autospawn lock."));
137
138             pa_unlock_lockfile(lf, lock_fd);
139             pa_xfree(lf);
140         }
141     }
142
143     lock_fd = -1;
144     state = STATE_IDLE;
145
146     pa_mutex_unlock(lock_fd_mutex);
147
148     pa_mutex_free(lock_fd_mutex);
149     lock_fd_mutex = NULL;
150
151     pa_close(pipe_fd[0]);
152     pa_close(pipe_fd[1]);
153     pipe_fd[0] = pipe_fd[1] = -1;
154 }
155
156 static void ping(void) {
157     ssize_t s;
158
159     pa_assert(pipe_fd[1] >= 0);
160
161     for (;;) {
162         char x = 'x';
163
164         if ((s = pa_write(pipe_fd[1], &x, 1, NULL)) == 1)
165             break;
166
167         pa_assert(s < 0);
168
169         if (errno == EAGAIN)
170             break;
171
172         pa_assert(errno == EINTR);
173     }
174 }
175
176 static void wait_for_ping(void) {
177     ssize_t s;
178     char x;
179     struct pollfd pfd;
180     int k;
181
182     pa_assert(pipe_fd[0] >= 0);
183
184     memset(&pfd, 0, sizeof(pfd));
185     pfd.fd = pipe_fd[0];
186     pfd.events = POLLIN;
187
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);
194     }
195 }
196
197 static void empty_pipe(void) {
198     char x[16];
199     ssize_t s;
200
201     pa_assert(pipe_fd[0] >= 0);
202
203     if ((s = pa_read(pipe_fd[0], &x, sizeof(x), NULL)) < 1) {
204         pa_assert(s < 0);
205         pa_assert(errno == EAGAIN);
206     }
207 }
208
209 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     sigfillset(&fullset);
218     pthread_sigmask(SIG_BLOCK, &fullset, NULL);
219 #endif
220
221     if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
222         pa_log_warn(_("Cannot access autospawn lock."));
223         goto fail;
224     }
225
226     if ((fd = pa_lock_lockfile(lf)) < 0)
227         goto fail;
228
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);
234
235     goto finish;
236
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);
242
243 finish:
244     pa_xfree(lf);
245
246     ping();
247 }
248
249 static int start_thread(void) {
250
251     if (!thread)
252         if (!(thread = pa_thread_new("autospawn", thread_func, NULL)))
253             return -1;
254
255     return 0;
256 }
257
258 static void create_mutex(void) {
259     PA_ONCE_BEGIN {
260         mutex = pa_mutex_new(false, false);
261     } PA_ONCE_END;
262 }
263
264 static void destroy_mutex(void) {
265     if (mutex)
266         pa_mutex_free(mutex);
267 }
268
269 int pa_autospawn_lock_init(void) {
270     int ret = -1;
271
272     create_mutex();
273     pa_mutex_lock(mutex);
274
275     if (ref() < 0)
276         ret = -1;
277     else
278         ret = pipe_fd[0];
279
280     pa_mutex_unlock(mutex);
281
282     return ret;
283 }
284
285 int pa_autospawn_lock_acquire(bool block) {
286     int ret = -1;
287
288     create_mutex();
289     pa_mutex_lock(mutex);
290     pa_assert(n_ref >= 1);
291
292     pa_mutex_lock(lock_fd_mutex);
293
294     for (;;) {
295
296         empty_pipe();
297
298         if (state == STATE_OWNING) {
299             state = STATE_TAKEN;
300             ret = 1;
301             break;
302         }
303
304         if (state == STATE_FAILED) {
305             ret = -1;
306             break;
307         }
308
309         if (state == STATE_IDLE)
310             if (start_thread() < 0)
311                 break;
312
313         if (!block) {
314             ret = 0;
315             break;
316         }
317
318         pa_mutex_unlock(lock_fd_mutex);
319         pa_mutex_unlock(mutex);
320
321         wait_for_ping();
322
323         pa_mutex_lock(mutex);
324         pa_mutex_lock(lock_fd_mutex);
325     }
326
327     pa_mutex_unlock(lock_fd_mutex);
328
329     pa_mutex_unlock(mutex);
330
331     return ret;
332 }
333
334 void pa_autospawn_lock_release(void) {
335
336     create_mutex();
337     pa_mutex_lock(mutex);
338     pa_assert(n_ref >= 1);
339
340     pa_assert(state == STATE_TAKEN);
341     state = STATE_OWNING;
342
343     ping();
344
345     pa_mutex_unlock(mutex);
346 }
347
348 void pa_autospawn_lock_done(bool after_fork) {
349
350     create_mutex();
351     pa_mutex_lock(mutex);
352     pa_assert(n_ref >= 1);
353
354     unref(after_fork);
355
356     pa_mutex_unlock(mutex);
357 }