153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2008 Lennart Poettering 553a5a1b3Sopenharmony_ci 653a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 753a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 853a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 953a5a1b3Sopenharmony_ci or (at your option) any later version. 1053a5a1b3Sopenharmony_ci 1153a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1253a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1353a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1453a5a1b3Sopenharmony_ci General Public License for more details. 1553a5a1b3Sopenharmony_ci 1653a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1753a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1853a5a1b3Sopenharmony_ci***/ 1953a5a1b3Sopenharmony_ci 2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2153a5a1b3Sopenharmony_ci#include <config.h> 2253a5a1b3Sopenharmony_ci#endif 2353a5a1b3Sopenharmony_ci 2453a5a1b3Sopenharmony_ci#include <errno.h> 2553a5a1b3Sopenharmony_ci#include <string.h> 2653a5a1b3Sopenharmony_ci#include <signal.h> 2753a5a1b3Sopenharmony_ci 2853a5a1b3Sopenharmony_ci#ifdef HAVE_PTHREAD 2953a5a1b3Sopenharmony_ci#include <pthread.h> 3053a5a1b3Sopenharmony_ci#endif 3153a5a1b3Sopenharmony_ci 3253a5a1b3Sopenharmony_ci#include <pulse/gccmacro.h> 3353a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 3453a5a1b3Sopenharmony_ci 3553a5a1b3Sopenharmony_ci#include <pulsecore/i18n.h> 3653a5a1b3Sopenharmony_ci#include <pulsecore/poll.h> 3753a5a1b3Sopenharmony_ci#include <pulsecore/mutex.h> 3853a5a1b3Sopenharmony_ci#include <pulsecore/thread.h> 3953a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 4053a5a1b3Sopenharmony_ci 4153a5a1b3Sopenharmony_ci#include "lock-autospawn.h" 4253a5a1b3Sopenharmony_ci 4353a5a1b3Sopenharmony_ci/* So, why do we have this complex code here with threads and pipes 4453a5a1b3Sopenharmony_ci * and stuff? For two reasons: POSIX file locks are per-process, not 4553a5a1b3Sopenharmony_ci * per-file descriptor. That means that two contexts within the same 4653a5a1b3Sopenharmony_ci * process that try to create the autospawn lock might end up assuming 4753a5a1b3Sopenharmony_ci * they both managed to lock the file. And then, POSIX locking 4853a5a1b3Sopenharmony_ci * operations are synchronous. If two contexts run from the same event 4953a5a1b3Sopenharmony_ci * loop it must be made sure that they do not block each other, but 5053a5a1b3Sopenharmony_ci * that the locking operation can happen asynchronously. */ 5153a5a1b3Sopenharmony_ci 5253a5a1b3Sopenharmony_ci#define AUTOSPAWN_LOCK "autospawn.lock" 5353a5a1b3Sopenharmony_ci 5453a5a1b3Sopenharmony_cistatic pa_mutex *mutex; 5553a5a1b3Sopenharmony_ci 5653a5a1b3Sopenharmony_cistatic unsigned n_ref = 0; 5753a5a1b3Sopenharmony_cistatic int lock_fd = -1; 5853a5a1b3Sopenharmony_cistatic pa_mutex *lock_fd_mutex = NULL; 5953a5a1b3Sopenharmony_cistatic pa_thread *thread = NULL; 6053a5a1b3Sopenharmony_cistatic int pipe_fd[2] = { -1, -1 }; 6153a5a1b3Sopenharmony_ci 6253a5a1b3Sopenharmony_cistatic enum { 6353a5a1b3Sopenharmony_ci STATE_IDLE, 6453a5a1b3Sopenharmony_ci STATE_OWNING, 6553a5a1b3Sopenharmony_ci STATE_TAKEN, 6653a5a1b3Sopenharmony_ci STATE_FAILED 6753a5a1b3Sopenharmony_ci} state = STATE_IDLE; 6853a5a1b3Sopenharmony_ci 6953a5a1b3Sopenharmony_cistatic void destroy_mutex(void) PA_GCC_DESTRUCTOR; 7053a5a1b3Sopenharmony_ci 7153a5a1b3Sopenharmony_cistatic int ref(void) { 7253a5a1b3Sopenharmony_ci 7353a5a1b3Sopenharmony_ci if (n_ref > 0) { 7453a5a1b3Sopenharmony_ci 7553a5a1b3Sopenharmony_ci pa_assert(pipe_fd[0] >= 0); 7653a5a1b3Sopenharmony_ci pa_assert(pipe_fd[1] >= 0); 7753a5a1b3Sopenharmony_ci pa_assert(lock_fd_mutex); 7853a5a1b3Sopenharmony_ci 7953a5a1b3Sopenharmony_ci n_ref++; 8053a5a1b3Sopenharmony_ci 8153a5a1b3Sopenharmony_ci return 0; 8253a5a1b3Sopenharmony_ci } 8353a5a1b3Sopenharmony_ci 8453a5a1b3Sopenharmony_ci pa_assert(!lock_fd_mutex); 8553a5a1b3Sopenharmony_ci pa_assert(state == STATE_IDLE); 8653a5a1b3Sopenharmony_ci pa_assert(lock_fd < 0); 8753a5a1b3Sopenharmony_ci pa_assert(!thread); 8853a5a1b3Sopenharmony_ci pa_assert(pipe_fd[0] < 0); 8953a5a1b3Sopenharmony_ci pa_assert(pipe_fd[1] < 0); 9053a5a1b3Sopenharmony_ci 9153a5a1b3Sopenharmony_ci if (pa_pipe_cloexec(pipe_fd) < 0) 9253a5a1b3Sopenharmony_ci return -1; 9353a5a1b3Sopenharmony_ci 9453a5a1b3Sopenharmony_ci pa_make_fd_nonblock(pipe_fd[1]); 9553a5a1b3Sopenharmony_ci pa_make_fd_nonblock(pipe_fd[0]); 9653a5a1b3Sopenharmony_ci 9753a5a1b3Sopenharmony_ci lock_fd_mutex = pa_mutex_new(false, false); 9853a5a1b3Sopenharmony_ci 9953a5a1b3Sopenharmony_ci n_ref = 1; 10053a5a1b3Sopenharmony_ci return 0; 10153a5a1b3Sopenharmony_ci} 10253a5a1b3Sopenharmony_ci 10353a5a1b3Sopenharmony_cistatic void unref(bool after_fork) { 10453a5a1b3Sopenharmony_ci 10553a5a1b3Sopenharmony_ci pa_assert(n_ref > 0); 10653a5a1b3Sopenharmony_ci pa_assert(pipe_fd[0] >= 0); 10753a5a1b3Sopenharmony_ci pa_assert(pipe_fd[1] >= 0); 10853a5a1b3Sopenharmony_ci pa_assert(lock_fd_mutex); 10953a5a1b3Sopenharmony_ci 11053a5a1b3Sopenharmony_ci n_ref--; 11153a5a1b3Sopenharmony_ci 11253a5a1b3Sopenharmony_ci if (n_ref > 0) 11353a5a1b3Sopenharmony_ci return; 11453a5a1b3Sopenharmony_ci 11553a5a1b3Sopenharmony_ci /* Join threads only in the process the new thread was created in 11653a5a1b3Sopenharmony_ci * to avoid undefined behaviour. 11753a5a1b3Sopenharmony_ci * POSIX.1-2008 XSH 2.9.2 Thread IDs: "applications should only assume 11853a5a1b3Sopenharmony_ci * that thread IDs are usable and unique within a single process." */ 11953a5a1b3Sopenharmony_ci if (thread) { 12053a5a1b3Sopenharmony_ci if (after_fork) 12153a5a1b3Sopenharmony_ci pa_thread_free_nojoin(thread); 12253a5a1b3Sopenharmony_ci else 12353a5a1b3Sopenharmony_ci pa_thread_free(thread); 12453a5a1b3Sopenharmony_ci thread = NULL; 12553a5a1b3Sopenharmony_ci } 12653a5a1b3Sopenharmony_ci 12753a5a1b3Sopenharmony_ci pa_mutex_lock(lock_fd_mutex); 12853a5a1b3Sopenharmony_ci 12953a5a1b3Sopenharmony_ci pa_assert(state != STATE_TAKEN); 13053a5a1b3Sopenharmony_ci 13153a5a1b3Sopenharmony_ci if (state == STATE_OWNING) { 13253a5a1b3Sopenharmony_ci 13353a5a1b3Sopenharmony_ci pa_assert(lock_fd >= 0); 13453a5a1b3Sopenharmony_ci 13553a5a1b3Sopenharmony_ci if (after_fork) 13653a5a1b3Sopenharmony_ci pa_close(lock_fd); 13753a5a1b3Sopenharmony_ci else { 13853a5a1b3Sopenharmony_ci char *lf; 13953a5a1b3Sopenharmony_ci 14053a5a1b3Sopenharmony_ci if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) 14153a5a1b3Sopenharmony_ci pa_log_warn(_("Cannot access autospawn lock.")); 14253a5a1b3Sopenharmony_ci 14353a5a1b3Sopenharmony_ci pa_unlock_lockfile(lf, lock_fd); 14453a5a1b3Sopenharmony_ci pa_xfree(lf); 14553a5a1b3Sopenharmony_ci } 14653a5a1b3Sopenharmony_ci } 14753a5a1b3Sopenharmony_ci 14853a5a1b3Sopenharmony_ci lock_fd = -1; 14953a5a1b3Sopenharmony_ci state = STATE_IDLE; 15053a5a1b3Sopenharmony_ci 15153a5a1b3Sopenharmony_ci pa_mutex_unlock(lock_fd_mutex); 15253a5a1b3Sopenharmony_ci 15353a5a1b3Sopenharmony_ci pa_mutex_free(lock_fd_mutex); 15453a5a1b3Sopenharmony_ci lock_fd_mutex = NULL; 15553a5a1b3Sopenharmony_ci 15653a5a1b3Sopenharmony_ci pa_close(pipe_fd[0]); 15753a5a1b3Sopenharmony_ci pa_close(pipe_fd[1]); 15853a5a1b3Sopenharmony_ci pipe_fd[0] = pipe_fd[1] = -1; 15953a5a1b3Sopenharmony_ci} 16053a5a1b3Sopenharmony_ci 16153a5a1b3Sopenharmony_cistatic void ping(void) { 16253a5a1b3Sopenharmony_ci ssize_t s; 16353a5a1b3Sopenharmony_ci 16453a5a1b3Sopenharmony_ci pa_assert(pipe_fd[1] >= 0); 16553a5a1b3Sopenharmony_ci 16653a5a1b3Sopenharmony_ci for (;;) { 16753a5a1b3Sopenharmony_ci char x = 'x'; 16853a5a1b3Sopenharmony_ci 16953a5a1b3Sopenharmony_ci if ((s = pa_write(pipe_fd[1], &x, 1, NULL)) == 1) 17053a5a1b3Sopenharmony_ci break; 17153a5a1b3Sopenharmony_ci 17253a5a1b3Sopenharmony_ci pa_assert(s < 0); 17353a5a1b3Sopenharmony_ci 17453a5a1b3Sopenharmony_ci if (errno == EAGAIN) 17553a5a1b3Sopenharmony_ci break; 17653a5a1b3Sopenharmony_ci 17753a5a1b3Sopenharmony_ci pa_assert(errno == EINTR); 17853a5a1b3Sopenharmony_ci } 17953a5a1b3Sopenharmony_ci} 18053a5a1b3Sopenharmony_ci 18153a5a1b3Sopenharmony_cistatic void wait_for_ping(void) { 18253a5a1b3Sopenharmony_ci ssize_t s; 18353a5a1b3Sopenharmony_ci char x; 18453a5a1b3Sopenharmony_ci struct pollfd pfd; 18553a5a1b3Sopenharmony_ci int k; 18653a5a1b3Sopenharmony_ci 18753a5a1b3Sopenharmony_ci pa_assert(pipe_fd[0] >= 0); 18853a5a1b3Sopenharmony_ci 18953a5a1b3Sopenharmony_ci memset(&pfd, 0, sizeof(pfd)); 19053a5a1b3Sopenharmony_ci pfd.fd = pipe_fd[0]; 19153a5a1b3Sopenharmony_ci pfd.events = POLLIN; 19253a5a1b3Sopenharmony_ci 19353a5a1b3Sopenharmony_ci if ((k = pa_poll(&pfd, 1, -1)) != 1) { 19453a5a1b3Sopenharmony_ci pa_assert(k < 0); 19553a5a1b3Sopenharmony_ci pa_assert(errno == EINTR); 19653a5a1b3Sopenharmony_ci } else if ((s = pa_read(pipe_fd[0], &x, 1, NULL)) != 1) { 19753a5a1b3Sopenharmony_ci pa_assert(s < 0); 19853a5a1b3Sopenharmony_ci pa_assert(errno == EAGAIN); 19953a5a1b3Sopenharmony_ci } 20053a5a1b3Sopenharmony_ci} 20153a5a1b3Sopenharmony_ci 20253a5a1b3Sopenharmony_cistatic void empty_pipe(void) { 20353a5a1b3Sopenharmony_ci char x[16]; 20453a5a1b3Sopenharmony_ci ssize_t s; 20553a5a1b3Sopenharmony_ci 20653a5a1b3Sopenharmony_ci pa_assert(pipe_fd[0] >= 0); 20753a5a1b3Sopenharmony_ci 20853a5a1b3Sopenharmony_ci if ((s = pa_read(pipe_fd[0], &x, sizeof(x), NULL)) < 1) { 20953a5a1b3Sopenharmony_ci pa_assert(s < 0); 21053a5a1b3Sopenharmony_ci pa_assert(errno == EAGAIN); 21153a5a1b3Sopenharmony_ci } 21253a5a1b3Sopenharmony_ci} 21353a5a1b3Sopenharmony_ci 21453a5a1b3Sopenharmony_cistatic void thread_func(void *u) { 21553a5a1b3Sopenharmony_ci int fd; 21653a5a1b3Sopenharmony_ci char *lf; 21753a5a1b3Sopenharmony_ci 21853a5a1b3Sopenharmony_ci#ifdef HAVE_PTHREAD 21953a5a1b3Sopenharmony_ci sigset_t fullset; 22053a5a1b3Sopenharmony_ci 22153a5a1b3Sopenharmony_ci /* No signals in this thread please */ 22253a5a1b3Sopenharmony_ci sigfillset(&fullset); 22353a5a1b3Sopenharmony_ci pthread_sigmask(SIG_BLOCK, &fullset, NULL); 22453a5a1b3Sopenharmony_ci#endif 22553a5a1b3Sopenharmony_ci 22653a5a1b3Sopenharmony_ci if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) { 22753a5a1b3Sopenharmony_ci pa_log_warn(_("Cannot access autospawn lock.")); 22853a5a1b3Sopenharmony_ci goto fail; 22953a5a1b3Sopenharmony_ci } 23053a5a1b3Sopenharmony_ci 23153a5a1b3Sopenharmony_ci if ((fd = pa_lock_lockfile(lf)) < 0) 23253a5a1b3Sopenharmony_ci goto fail; 23353a5a1b3Sopenharmony_ci 23453a5a1b3Sopenharmony_ci pa_mutex_lock(lock_fd_mutex); 23553a5a1b3Sopenharmony_ci pa_assert(state == STATE_IDLE); 23653a5a1b3Sopenharmony_ci lock_fd = fd; 23753a5a1b3Sopenharmony_ci state = STATE_OWNING; 23853a5a1b3Sopenharmony_ci pa_mutex_unlock(lock_fd_mutex); 23953a5a1b3Sopenharmony_ci 24053a5a1b3Sopenharmony_ci goto finish; 24153a5a1b3Sopenharmony_ci 24253a5a1b3Sopenharmony_cifail: 24353a5a1b3Sopenharmony_ci pa_mutex_lock(lock_fd_mutex); 24453a5a1b3Sopenharmony_ci pa_assert(state == STATE_IDLE); 24553a5a1b3Sopenharmony_ci state = STATE_FAILED; 24653a5a1b3Sopenharmony_ci pa_mutex_unlock(lock_fd_mutex); 24753a5a1b3Sopenharmony_ci 24853a5a1b3Sopenharmony_cifinish: 24953a5a1b3Sopenharmony_ci pa_xfree(lf); 25053a5a1b3Sopenharmony_ci 25153a5a1b3Sopenharmony_ci ping(); 25253a5a1b3Sopenharmony_ci} 25353a5a1b3Sopenharmony_ci 25453a5a1b3Sopenharmony_cistatic int start_thread(void) { 25553a5a1b3Sopenharmony_ci 25653a5a1b3Sopenharmony_ci if (!thread) 25753a5a1b3Sopenharmony_ci if (!(thread = pa_thread_new("autospawn", thread_func, NULL))) 25853a5a1b3Sopenharmony_ci return -1; 25953a5a1b3Sopenharmony_ci 26053a5a1b3Sopenharmony_ci return 0; 26153a5a1b3Sopenharmony_ci} 26253a5a1b3Sopenharmony_ci 26353a5a1b3Sopenharmony_cistatic void create_mutex(void) { 26453a5a1b3Sopenharmony_ci PA_ONCE_BEGIN { 26553a5a1b3Sopenharmony_ci mutex = pa_mutex_new(false, false); 26653a5a1b3Sopenharmony_ci } PA_ONCE_END; 26753a5a1b3Sopenharmony_ci} 26853a5a1b3Sopenharmony_ci 26953a5a1b3Sopenharmony_cistatic void destroy_mutex(void) { 27053a5a1b3Sopenharmony_ci if (mutex) 27153a5a1b3Sopenharmony_ci pa_mutex_free(mutex); 27253a5a1b3Sopenharmony_ci} 27353a5a1b3Sopenharmony_ci 27453a5a1b3Sopenharmony_ciint pa_autospawn_lock_init(void) { 27553a5a1b3Sopenharmony_ci int ret = -1; 27653a5a1b3Sopenharmony_ci 27753a5a1b3Sopenharmony_ci create_mutex(); 27853a5a1b3Sopenharmony_ci pa_mutex_lock(mutex); 27953a5a1b3Sopenharmony_ci 28053a5a1b3Sopenharmony_ci if (ref() < 0) 28153a5a1b3Sopenharmony_ci ret = -1; 28253a5a1b3Sopenharmony_ci else 28353a5a1b3Sopenharmony_ci ret = pipe_fd[0]; 28453a5a1b3Sopenharmony_ci 28553a5a1b3Sopenharmony_ci pa_mutex_unlock(mutex); 28653a5a1b3Sopenharmony_ci 28753a5a1b3Sopenharmony_ci return ret; 28853a5a1b3Sopenharmony_ci} 28953a5a1b3Sopenharmony_ci 29053a5a1b3Sopenharmony_ciint pa_autospawn_lock_acquire(bool block) { 29153a5a1b3Sopenharmony_ci int ret = -1; 29253a5a1b3Sopenharmony_ci 29353a5a1b3Sopenharmony_ci create_mutex(); 29453a5a1b3Sopenharmony_ci pa_mutex_lock(mutex); 29553a5a1b3Sopenharmony_ci pa_assert(n_ref >= 1); 29653a5a1b3Sopenharmony_ci 29753a5a1b3Sopenharmony_ci pa_mutex_lock(lock_fd_mutex); 29853a5a1b3Sopenharmony_ci 29953a5a1b3Sopenharmony_ci for (;;) { 30053a5a1b3Sopenharmony_ci 30153a5a1b3Sopenharmony_ci empty_pipe(); 30253a5a1b3Sopenharmony_ci 30353a5a1b3Sopenharmony_ci if (state == STATE_OWNING) { 30453a5a1b3Sopenharmony_ci state = STATE_TAKEN; 30553a5a1b3Sopenharmony_ci ret = 1; 30653a5a1b3Sopenharmony_ci break; 30753a5a1b3Sopenharmony_ci } 30853a5a1b3Sopenharmony_ci 30953a5a1b3Sopenharmony_ci if (state == STATE_FAILED) { 31053a5a1b3Sopenharmony_ci ret = -1; 31153a5a1b3Sopenharmony_ci break; 31253a5a1b3Sopenharmony_ci } 31353a5a1b3Sopenharmony_ci 31453a5a1b3Sopenharmony_ci if (state == STATE_IDLE) 31553a5a1b3Sopenharmony_ci if (start_thread() < 0) 31653a5a1b3Sopenharmony_ci break; 31753a5a1b3Sopenharmony_ci 31853a5a1b3Sopenharmony_ci if (!block) { 31953a5a1b3Sopenharmony_ci ret = 0; 32053a5a1b3Sopenharmony_ci break; 32153a5a1b3Sopenharmony_ci } 32253a5a1b3Sopenharmony_ci 32353a5a1b3Sopenharmony_ci pa_mutex_unlock(lock_fd_mutex); 32453a5a1b3Sopenharmony_ci pa_mutex_unlock(mutex); 32553a5a1b3Sopenharmony_ci 32653a5a1b3Sopenharmony_ci wait_for_ping(); 32753a5a1b3Sopenharmony_ci 32853a5a1b3Sopenharmony_ci pa_mutex_lock(mutex); 32953a5a1b3Sopenharmony_ci pa_mutex_lock(lock_fd_mutex); 33053a5a1b3Sopenharmony_ci } 33153a5a1b3Sopenharmony_ci 33253a5a1b3Sopenharmony_ci pa_mutex_unlock(lock_fd_mutex); 33353a5a1b3Sopenharmony_ci 33453a5a1b3Sopenharmony_ci pa_mutex_unlock(mutex); 33553a5a1b3Sopenharmony_ci 33653a5a1b3Sopenharmony_ci return ret; 33753a5a1b3Sopenharmony_ci} 33853a5a1b3Sopenharmony_ci 33953a5a1b3Sopenharmony_civoid pa_autospawn_lock_release(void) { 34053a5a1b3Sopenharmony_ci 34153a5a1b3Sopenharmony_ci create_mutex(); 34253a5a1b3Sopenharmony_ci pa_mutex_lock(mutex); 34353a5a1b3Sopenharmony_ci pa_assert(n_ref >= 1); 34453a5a1b3Sopenharmony_ci 34553a5a1b3Sopenharmony_ci pa_assert(state == STATE_TAKEN); 34653a5a1b3Sopenharmony_ci state = STATE_OWNING; 34753a5a1b3Sopenharmony_ci 34853a5a1b3Sopenharmony_ci ping(); 34953a5a1b3Sopenharmony_ci 35053a5a1b3Sopenharmony_ci pa_mutex_unlock(mutex); 35153a5a1b3Sopenharmony_ci} 35253a5a1b3Sopenharmony_ci 35353a5a1b3Sopenharmony_civoid pa_autospawn_lock_done(bool after_fork) { 35453a5a1b3Sopenharmony_ci 35553a5a1b3Sopenharmony_ci create_mutex(); 35653a5a1b3Sopenharmony_ci pa_mutex_lock(mutex); 35753a5a1b3Sopenharmony_ci pa_assert(n_ref >= 1); 35853a5a1b3Sopenharmony_ci 35953a5a1b3Sopenharmony_ci unref(after_fork); 36053a5a1b3Sopenharmony_ci 36153a5a1b3Sopenharmony_ci pa_mutex_unlock(mutex); 36253a5a1b3Sopenharmony_ci} 363