Merge commit 'origin/master-tx'
[profile/ivi/pulseaudio-panda.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_bool_t taken = FALSE;
59 static pa_thread *thread;
60 static int pipe_fd[2] = { -1, -1 };
61
62 static void destroy_mutex(void) PA_GCC_DESTRUCTOR;
63
64 static int ref(void) {
65
66     if (n_ref > 0) {
67
68         pa_assert(pipe_fd[0] >= 0);
69         pa_assert(pipe_fd[1] >= 0);
70
71         n_ref++;
72
73         return 0;
74     }
75
76     pa_assert(lock_fd < 0);
77     pa_assert(!lock_fd_mutex);
78     pa_assert(!taken);
79     pa_assert(!thread);
80     pa_assert(pipe_fd[0] < 0);
81     pa_assert(pipe_fd[1] < 0);
82
83     if (pipe(pipe_fd) < 0)
84         return -1;
85
86     lock_fd_mutex = pa_mutex_new(FALSE, FALSE);
87
88     pa_make_fd_cloexec(pipe_fd[0]);
89     pa_make_fd_cloexec(pipe_fd[1]);
90
91     pa_make_fd_nonblock(pipe_fd[1]);
92     pa_make_fd_nonblock(pipe_fd[0]);
93
94     n_ref = 1;
95     return 0;
96 }
97
98 static void unref(pa_bool_t after_fork) {
99
100     pa_assert(n_ref > 0);
101     pa_assert(pipe_fd[0] >= 0);
102     pa_assert(pipe_fd[1] >= 0);
103     pa_assert(lock_fd_mutex);
104
105     n_ref--;
106
107     if (n_ref > 0)
108         return;
109
110     pa_assert(!taken);
111
112     if (thread) {
113         pa_thread_free(thread);
114         thread = NULL;
115     }
116
117     pa_mutex_lock(lock_fd_mutex);
118     if (lock_fd >= 0) {
119
120         if (after_fork)
121             pa_close(lock_fd);
122         else {
123             char *lf;
124
125             if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
126                 pa_log_warn(_("Cannot access autospawn lock."));
127
128             pa_unlock_lockfile(lf, lock_fd);
129             pa_xfree(lf);
130
131             lock_fd = -1;
132         }
133     }
134     pa_mutex_unlock(lock_fd_mutex);
135
136     pa_mutex_free(lock_fd_mutex);
137     lock_fd_mutex = NULL;
138
139     pa_close(pipe_fd[0]);
140     pa_close(pipe_fd[1]);
141     pipe_fd[0] = pipe_fd[1] = -1;
142 }
143
144 static void ping(void) {
145     ssize_t s;
146
147     pa_assert(pipe_fd[1] >= 0);
148
149     for (;;) {
150         char x = 'x';
151
152         if ((s = write(pipe_fd[1], &x, 1)) == 1)
153             break;
154
155         pa_assert(s < 0);
156
157         if (errno == EAGAIN)
158             break;
159
160         pa_assert(errno == EINTR);
161     }
162 }
163
164 static void wait_for_ping(void) {
165     ssize_t s;
166     char x;
167     struct pollfd pfd;
168     int k;
169
170     pa_assert(pipe_fd[0] >= 0);
171
172     memset(&pfd, 0, sizeof(pfd));
173     pfd.fd = pipe_fd[0];
174     pfd.events = POLLIN;
175
176     if ((k = poll(&pfd, 1, -1)) != 1) {
177         pa_assert(k < 0);
178         pa_assert(errno == EINTR);
179     } else if ((s = read(pipe_fd[0], &x, 1)) != 1) {
180         pa_assert(s < 0);
181         pa_assert(errno == EAGAIN);
182     }
183 }
184
185 static void empty_pipe(void) {
186     char x[16];
187     ssize_t s;
188
189     pa_assert(pipe_fd[0] >= 0);
190
191     if ((s = read(pipe_fd[0], &x, sizeof(x))) < 1) {
192         pa_assert(s < 0);
193         pa_assert(errno == EAGAIN);
194     }
195 }
196
197 static void thread_func(void *u) {
198     int fd;
199     char *lf;
200     sigset_t fullset;
201
202     /* No signals in this thread please */
203     sigfillset(&fullset);
204     pthread_sigmask(SIG_BLOCK, &fullset, NULL);
205
206     if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
207         pa_log_warn(_("Cannot access autospawn lock."));
208         goto finish;
209     }
210
211     if ((fd = pa_lock_lockfile(lf)) < 0)
212         goto finish;
213
214     pa_mutex_lock(lock_fd_mutex);
215     pa_assert(lock_fd < 0);
216     lock_fd = fd;
217     pa_mutex_unlock(lock_fd_mutex);
218
219 finish:
220     pa_xfree(lf);
221
222     ping();
223 }
224
225 static int start_thread(void) {
226
227     if (!thread)
228         if (!(thread = pa_thread_new(thread_func, NULL)))
229             return -1;
230
231     return 0;
232 }
233
234 static void create_mutex(void) {
235     PA_ONCE_BEGIN {
236         mutex = pa_mutex_new(FALSE, FALSE);
237     } PA_ONCE_END;
238 }
239
240 static void destroy_mutex(void) {
241
242     if (mutex)
243         pa_mutex_free(mutex);
244 }
245
246
247 int pa_autospawn_lock_init(void) {
248     int ret = -1;
249
250     create_mutex();
251     pa_mutex_lock(mutex);
252
253     if (ref() < 0)
254         ret = -1;
255     else
256         ret = pipe_fd[0];
257
258     pa_mutex_unlock(mutex);
259
260     return ret;
261 }
262
263 int pa_autospawn_lock_acquire(pa_bool_t block) {
264     int ret = -1;
265
266     create_mutex();
267     pa_mutex_lock(mutex);
268     pa_assert(n_ref >= 1);
269
270     pa_mutex_lock(lock_fd_mutex);
271
272     for (;;) {
273
274         empty_pipe();
275
276         if (lock_fd >= 0 && !taken) {
277             taken = TRUE;
278             ret = 1;
279             break;
280         }
281
282         if (lock_fd < 0)
283             if (start_thread() < 0)
284                 break;
285
286         if (!block) {
287             ret = 0;
288             break;
289         }
290
291         pa_mutex_unlock(lock_fd_mutex);
292         pa_mutex_unlock(mutex);
293
294         wait_for_ping();
295
296         pa_mutex_lock(mutex);
297         pa_mutex_lock(lock_fd_mutex);
298     }
299
300     pa_mutex_unlock(lock_fd_mutex);
301
302     pa_mutex_unlock(mutex);
303
304     return ret;
305 }
306
307 void pa_autospawn_lock_release(void) {
308
309     create_mutex();
310     pa_mutex_lock(mutex);
311     pa_assert(n_ref >= 1);
312
313     pa_assert(taken);
314     taken = FALSE;
315
316     ping();
317
318     pa_mutex_unlock(mutex);
319 }
320
321 void pa_autospawn_lock_done(pa_bool_t after_fork) {
322
323     create_mutex();
324     pa_mutex_lock(mutex);
325     pa_assert(n_ref >= 1);
326
327     unref(after_fork);
328
329     pa_mutex_unlock(mutex);
330 }