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