]> code.delx.au - pulseaudio/blob - src/pulsecore/lock-autospawn.c
remap: Change remapping function argument type from void to int16_t / float as approp...
[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 <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 /* Join threads only in the process the new thread was created in
118 * to avoid undefined behaviour.
119 * POSIX.1-2008 XSH 2.9.2 Thread IDs: "applications should only assume
120 * that thread IDs are usable and unique within a single process." */
121 if (thread) {
122 if (after_fork)
123 pa_thread_free_nojoin(thread);
124 else
125 pa_thread_free(thread);
126 thread = NULL;
127 }
128
129 pa_mutex_lock(lock_fd_mutex);
130
131 pa_assert(state != STATE_TAKEN);
132
133 if (state == STATE_OWNING) {
134
135 pa_assert(lock_fd >= 0);
136
137 if (after_fork)
138 pa_close(lock_fd);
139 else {
140 char *lf;
141
142 if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
143 pa_log_warn(_("Cannot access autospawn lock."));
144
145 pa_unlock_lockfile(lf, lock_fd);
146 pa_xfree(lf);
147 }
148 }
149
150 lock_fd = -1;
151 state = STATE_IDLE;
152
153 pa_mutex_unlock(lock_fd_mutex);
154
155 pa_mutex_free(lock_fd_mutex);
156 lock_fd_mutex = NULL;
157
158 pa_close(pipe_fd[0]);
159 pa_close(pipe_fd[1]);
160 pipe_fd[0] = pipe_fd[1] = -1;
161 }
162
163 static void ping(void) {
164 ssize_t s;
165
166 pa_assert(pipe_fd[1] >= 0);
167
168 for (;;) {
169 char x = 'x';
170
171 if ((s = pa_write(pipe_fd[1], &x, 1, NULL)) == 1)
172 break;
173
174 pa_assert(s < 0);
175
176 if (errno == EAGAIN)
177 break;
178
179 pa_assert(errno == EINTR);
180 }
181 }
182
183 static void wait_for_ping(void) {
184 ssize_t s;
185 char x;
186 struct pollfd pfd;
187 int k;
188
189 pa_assert(pipe_fd[0] >= 0);
190
191 memset(&pfd, 0, sizeof(pfd));
192 pfd.fd = pipe_fd[0];
193 pfd.events = POLLIN;
194
195 if ((k = pa_poll(&pfd, 1, -1)) != 1) {
196 pa_assert(k < 0);
197 pa_assert(errno == EINTR);
198 } else if ((s = pa_read(pipe_fd[0], &x, 1, NULL)) != 1) {
199 pa_assert(s < 0);
200 pa_assert(errno == EAGAIN);
201 }
202 }
203
204 static void empty_pipe(void) {
205 char x[16];
206 ssize_t s;
207
208 pa_assert(pipe_fd[0] >= 0);
209
210 if ((s = pa_read(pipe_fd[0], &x, sizeof(x), NULL)) < 1) {
211 pa_assert(s < 0);
212 pa_assert(errno == EAGAIN);
213 }
214 }
215
216 static void thread_func(void *u) {
217 int fd;
218 char *lf;
219
220 #ifdef HAVE_PTHREAD
221 sigset_t fullset;
222
223 /* No signals in this thread please */
224 sigfillset(&fullset);
225 pthread_sigmask(SIG_BLOCK, &fullset, NULL);
226 #endif
227
228 if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
229 pa_log_warn(_("Cannot access autospawn lock."));
230 goto fail;
231 }
232
233 if ((fd = pa_lock_lockfile(lf)) < 0)
234 goto fail;
235
236 pa_mutex_lock(lock_fd_mutex);
237 pa_assert(state == STATE_IDLE);
238 lock_fd = fd;
239 state = STATE_OWNING;
240 pa_mutex_unlock(lock_fd_mutex);
241
242 goto finish;
243
244 fail:
245 pa_mutex_lock(lock_fd_mutex);
246 pa_assert(state == STATE_IDLE);
247 state = STATE_FAILED;
248 pa_mutex_unlock(lock_fd_mutex);
249
250 finish:
251 pa_xfree(lf);
252
253 ping();
254 }
255
256 static int start_thread(void) {
257
258 if (!thread)
259 if (!(thread = pa_thread_new("autospawn", thread_func, NULL)))
260 return -1;
261
262 return 0;
263 }
264
265 static void create_mutex(void) {
266 PA_ONCE_BEGIN {
267 mutex = pa_mutex_new(false, false);
268 } PA_ONCE_END;
269 }
270
271 static void destroy_mutex(void) {
272 if (mutex)
273 pa_mutex_free(mutex);
274 }
275
276 int pa_autospawn_lock_init(void) {
277 int ret = -1;
278
279 create_mutex();
280 pa_mutex_lock(mutex);
281
282 if (ref() < 0)
283 ret = -1;
284 else
285 ret = pipe_fd[0];
286
287 pa_mutex_unlock(mutex);
288
289 return ret;
290 }
291
292 int pa_autospawn_lock_acquire(bool block) {
293 int ret = -1;
294
295 create_mutex();
296 pa_mutex_lock(mutex);
297 pa_assert(n_ref >= 1);
298
299 pa_mutex_lock(lock_fd_mutex);
300
301 for (;;) {
302
303 empty_pipe();
304
305 if (state == STATE_OWNING) {
306 state = STATE_TAKEN;
307 ret = 1;
308 break;
309 }
310
311 if (state == STATE_FAILED) {
312 ret = -1;
313 break;
314 }
315
316 if (state == STATE_IDLE)
317 if (start_thread() < 0)
318 break;
319
320 if (!block) {
321 ret = 0;
322 break;
323 }
324
325 pa_mutex_unlock(lock_fd_mutex);
326 pa_mutex_unlock(mutex);
327
328 wait_for_ping();
329
330 pa_mutex_lock(mutex);
331 pa_mutex_lock(lock_fd_mutex);
332 }
333
334 pa_mutex_unlock(lock_fd_mutex);
335
336 pa_mutex_unlock(mutex);
337
338 return ret;
339 }
340
341 void pa_autospawn_lock_release(void) {
342
343 create_mutex();
344 pa_mutex_lock(mutex);
345 pa_assert(n_ref >= 1);
346
347 pa_assert(state == STATE_TAKEN);
348 state = STATE_OWNING;
349
350 ping();
351
352 pa_mutex_unlock(mutex);
353 }
354
355 void pa_autospawn_lock_done(bool after_fork) {
356
357 create_mutex();
358 pa_mutex_lock(mutex);
359 pa_assert(n_ref >= 1);
360
361 unref(after_fork);
362
363 pa_mutex_unlock(mutex);
364 }