]> code.delx.au - pulseaudio/blob - src/pulsecore/lock-autospawn.c
remap: fix build for non-x86 builds
[pulseaudio] / 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 (pipe(pipe_fd) < 0)
91 return -1;
92
93 pa_make_fd_cloexec(pipe_fd[0]);
94 pa_make_fd_cloexec(pipe_fd[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(pa_bool_t 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 = write(pipe_fd[1], &x, 1)) == 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 = poll(&pfd, 1, -1)) != 1) {
189 pa_assert(k < 0);
190 pa_assert(errno == EINTR);
191 } else if ((s = read(pipe_fd[0], &x, 1)) != 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 = read(pipe_fd[0], &x, sizeof(x))) < 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 sigset_t fullset;
213
214 /* No signals in this thread please */
215 sigfillset(&fullset);
216 pthread_sigmask(SIG_BLOCK, &fullset, NULL);
217
218 if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
219 pa_log_warn(_("Cannot access autospawn lock."));
220 goto fail;
221 }
222
223 if ((fd = pa_lock_lockfile(lf)) < 0)
224 goto fail;
225
226 pa_mutex_lock(lock_fd_mutex);
227 pa_assert(state == STATE_IDLE);
228 lock_fd = fd;
229 state = STATE_OWNING;
230 pa_mutex_unlock(lock_fd_mutex);
231
232 goto finish;
233
234 fail:
235 pa_mutex_lock(lock_fd_mutex);
236 pa_assert(state == STATE_IDLE);
237 state = STATE_FAILED;
238 pa_mutex_unlock(lock_fd_mutex);
239
240 finish:
241 pa_xfree(lf);
242
243 ping();
244 }
245
246 static int start_thread(void) {
247
248 if (!thread)
249 if (!(thread = pa_thread_new(thread_func, NULL)))
250 return -1;
251
252 return 0;
253 }
254
255 static void create_mutex(void) {
256 PA_ONCE_BEGIN {
257 mutex = pa_mutex_new(FALSE, FALSE);
258 } PA_ONCE_END;
259 }
260
261 static void destroy_mutex(void) {
262 if (mutex)
263 pa_mutex_free(mutex);
264 }
265
266 int pa_autospawn_lock_init(void) {
267 int ret = -1;
268
269 create_mutex();
270 pa_mutex_lock(mutex);
271
272 if (ref() < 0)
273 ret = -1;
274 else
275 ret = pipe_fd[0];
276
277 pa_mutex_unlock(mutex);
278
279 return ret;
280 }
281
282 int pa_autospawn_lock_acquire(pa_bool_t block) {
283 int ret = -1;
284
285 create_mutex();
286 pa_mutex_lock(mutex);
287 pa_assert(n_ref >= 1);
288
289 pa_mutex_lock(lock_fd_mutex);
290
291 for (;;) {
292
293 empty_pipe();
294
295 if (state == STATE_OWNING) {
296 state = STATE_TAKEN;
297 ret = 1;
298 break;
299 }
300
301 if (state == STATE_FAILED) {
302 ret = -1;
303 break;
304 }
305
306 if (state == STATE_IDLE)
307 if (start_thread() < 0)
308 break;
309
310 if (!block) {
311 ret = 0;
312 break;
313 }
314
315 pa_mutex_unlock(lock_fd_mutex);
316 pa_mutex_unlock(mutex);
317
318 wait_for_ping();
319
320 pa_mutex_lock(mutex);
321 pa_mutex_lock(lock_fd_mutex);
322 }
323
324 pa_mutex_unlock(lock_fd_mutex);
325
326 pa_mutex_unlock(mutex);
327
328 return ret;
329 }
330
331 void pa_autospawn_lock_release(void) {
332
333 create_mutex();
334 pa_mutex_lock(mutex);
335 pa_assert(n_ref >= 1);
336
337 pa_assert(state == STATE_TAKEN);
338 state = STATE_OWNING;
339
340 ping();
341
342 pa_mutex_unlock(mutex);
343 }
344
345 void pa_autospawn_lock_done(pa_bool_t after_fork) {
346
347 create_mutex();
348 pa_mutex_lock(mutex);
349 pa_assert(n_ref >= 1);
350
351 unref(after_fork);
352
353 pa_mutex_unlock(mutex);
354 }