mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-12 13:30:10 -05:00
FSF addresses used in PA sources are no longer valid and rpmlint generates numerous warnings during packaging because of this. This patch changes all FSF addresses to FSF web page according to the GPL how-to: https://www.gnu.org/licenses/gpl-howto.en.html Done automatically by sed-ing through sources.
362 lines
7.3 KiB
C
362 lines
7.3 KiB
C
/***
|
|
This file is part of PulseAudio.
|
|
|
|
Copyright 2008 Lennart Poettering
|
|
|
|
PulseAudio is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published
|
|
by the Free Software Foundation; either version 2.1 of the License,
|
|
or (at your option) any later version.
|
|
|
|
PulseAudio is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
|
***/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
|
|
#ifdef HAVE_PTHREAD
|
|
#include <pthread.h>
|
|
#endif
|
|
|
|
#include <pulse/gccmacro.h>
|
|
#include <pulse/xmalloc.h>
|
|
|
|
#include <pulsecore/i18n.h>
|
|
#include <pulsecore/poll.h>
|
|
#include <pulsecore/mutex.h>
|
|
#include <pulsecore/thread.h>
|
|
#include <pulsecore/core-util.h>
|
|
|
|
#include "lock-autospawn.h"
|
|
|
|
/* So, why do we have this complex code here with threads and pipes
|
|
* and stuff? For two reasons: POSIX file locks are per-process, not
|
|
* per-file descriptor. That means that two contexts within the same
|
|
* process that try to create the autospawn lock might end up assuming
|
|
* they both managed to lock the file. And then, POSIX locking
|
|
* operations are synchronous. If two contexts run from the same event
|
|
* loop it must be made sure that they do not block each other, but
|
|
* that the locking operation can happen asynchronously. */
|
|
|
|
#define AUTOSPAWN_LOCK "autospawn.lock"
|
|
|
|
static pa_mutex *mutex;
|
|
|
|
static unsigned n_ref = 0;
|
|
static int lock_fd = -1;
|
|
static pa_mutex *lock_fd_mutex = NULL;
|
|
static pa_thread *thread = NULL;
|
|
static int pipe_fd[2] = { -1, -1 };
|
|
|
|
static enum {
|
|
STATE_IDLE,
|
|
STATE_OWNING,
|
|
STATE_TAKEN,
|
|
STATE_FAILED
|
|
} state = STATE_IDLE;
|
|
|
|
static void destroy_mutex(void) PA_GCC_DESTRUCTOR;
|
|
|
|
static int ref(void) {
|
|
|
|
if (n_ref > 0) {
|
|
|
|
pa_assert(pipe_fd[0] >= 0);
|
|
pa_assert(pipe_fd[1] >= 0);
|
|
pa_assert(lock_fd_mutex);
|
|
|
|
n_ref++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
pa_assert(!lock_fd_mutex);
|
|
pa_assert(state == STATE_IDLE);
|
|
pa_assert(lock_fd < 0);
|
|
pa_assert(!thread);
|
|
pa_assert(pipe_fd[0] < 0);
|
|
pa_assert(pipe_fd[1] < 0);
|
|
|
|
if (pa_pipe_cloexec(pipe_fd) < 0)
|
|
return -1;
|
|
|
|
pa_make_fd_nonblock(pipe_fd[1]);
|
|
pa_make_fd_nonblock(pipe_fd[0]);
|
|
|
|
lock_fd_mutex = pa_mutex_new(false, false);
|
|
|
|
n_ref = 1;
|
|
return 0;
|
|
}
|
|
|
|
static void unref(bool after_fork) {
|
|
|
|
pa_assert(n_ref > 0);
|
|
pa_assert(pipe_fd[0] >= 0);
|
|
pa_assert(pipe_fd[1] >= 0);
|
|
pa_assert(lock_fd_mutex);
|
|
|
|
n_ref--;
|
|
|
|
if (n_ref > 0)
|
|
return;
|
|
|
|
/* Join threads only in the process the new thread was created in
|
|
* to avoid undefined behaviour.
|
|
* POSIX.1-2008 XSH 2.9.2 Thread IDs: "applications should only assume
|
|
* that thread IDs are usable and unique within a single process." */
|
|
if (thread) {
|
|
if (after_fork)
|
|
pa_thread_free_nojoin(thread);
|
|
else
|
|
pa_thread_free(thread);
|
|
thread = NULL;
|
|
}
|
|
|
|
pa_mutex_lock(lock_fd_mutex);
|
|
|
|
pa_assert(state != STATE_TAKEN);
|
|
|
|
if (state == STATE_OWNING) {
|
|
|
|
pa_assert(lock_fd >= 0);
|
|
|
|
if (after_fork)
|
|
pa_close(lock_fd);
|
|
else {
|
|
char *lf;
|
|
|
|
if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
|
|
pa_log_warn(_("Cannot access autospawn lock."));
|
|
|
|
pa_unlock_lockfile(lf, lock_fd);
|
|
pa_xfree(lf);
|
|
}
|
|
}
|
|
|
|
lock_fd = -1;
|
|
state = STATE_IDLE;
|
|
|
|
pa_mutex_unlock(lock_fd_mutex);
|
|
|
|
pa_mutex_free(lock_fd_mutex);
|
|
lock_fd_mutex = NULL;
|
|
|
|
pa_close(pipe_fd[0]);
|
|
pa_close(pipe_fd[1]);
|
|
pipe_fd[0] = pipe_fd[1] = -1;
|
|
}
|
|
|
|
static void ping(void) {
|
|
ssize_t s;
|
|
|
|
pa_assert(pipe_fd[1] >= 0);
|
|
|
|
for (;;) {
|
|
char x = 'x';
|
|
|
|
if ((s = pa_write(pipe_fd[1], &x, 1, NULL)) == 1)
|
|
break;
|
|
|
|
pa_assert(s < 0);
|
|
|
|
if (errno == EAGAIN)
|
|
break;
|
|
|
|
pa_assert(errno == EINTR);
|
|
}
|
|
}
|
|
|
|
static void wait_for_ping(void) {
|
|
ssize_t s;
|
|
char x;
|
|
struct pollfd pfd;
|
|
int k;
|
|
|
|
pa_assert(pipe_fd[0] >= 0);
|
|
|
|
memset(&pfd, 0, sizeof(pfd));
|
|
pfd.fd = pipe_fd[0];
|
|
pfd.events = POLLIN;
|
|
|
|
if ((k = pa_poll(&pfd, 1, -1)) != 1) {
|
|
pa_assert(k < 0);
|
|
pa_assert(errno == EINTR);
|
|
} else if ((s = pa_read(pipe_fd[0], &x, 1, NULL)) != 1) {
|
|
pa_assert(s < 0);
|
|
pa_assert(errno == EAGAIN);
|
|
}
|
|
}
|
|
|
|
static void empty_pipe(void) {
|
|
char x[16];
|
|
ssize_t s;
|
|
|
|
pa_assert(pipe_fd[0] >= 0);
|
|
|
|
if ((s = pa_read(pipe_fd[0], &x, sizeof(x), NULL)) < 1) {
|
|
pa_assert(s < 0);
|
|
pa_assert(errno == EAGAIN);
|
|
}
|
|
}
|
|
|
|
static void thread_func(void *u) {
|
|
int fd;
|
|
char *lf;
|
|
|
|
#ifdef HAVE_PTHREAD
|
|
sigset_t fullset;
|
|
|
|
/* No signals in this thread please */
|
|
sigfillset(&fullset);
|
|
pthread_sigmask(SIG_BLOCK, &fullset, NULL);
|
|
#endif
|
|
|
|
if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
|
|
pa_log_warn(_("Cannot access autospawn lock."));
|
|
goto fail;
|
|
}
|
|
|
|
if ((fd = pa_lock_lockfile(lf)) < 0)
|
|
goto fail;
|
|
|
|
pa_mutex_lock(lock_fd_mutex);
|
|
pa_assert(state == STATE_IDLE);
|
|
lock_fd = fd;
|
|
state = STATE_OWNING;
|
|
pa_mutex_unlock(lock_fd_mutex);
|
|
|
|
goto finish;
|
|
|
|
fail:
|
|
pa_mutex_lock(lock_fd_mutex);
|
|
pa_assert(state == STATE_IDLE);
|
|
state = STATE_FAILED;
|
|
pa_mutex_unlock(lock_fd_mutex);
|
|
|
|
finish:
|
|
pa_xfree(lf);
|
|
|
|
ping();
|
|
}
|
|
|
|
static int start_thread(void) {
|
|
|
|
if (!thread)
|
|
if (!(thread = pa_thread_new("autospawn", thread_func, NULL)))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void create_mutex(void) {
|
|
PA_ONCE_BEGIN {
|
|
mutex = pa_mutex_new(false, false);
|
|
} PA_ONCE_END;
|
|
}
|
|
|
|
static void destroy_mutex(void) {
|
|
if (mutex)
|
|
pa_mutex_free(mutex);
|
|
}
|
|
|
|
int pa_autospawn_lock_init(void) {
|
|
int ret = -1;
|
|
|
|
create_mutex();
|
|
pa_mutex_lock(mutex);
|
|
|
|
if (ref() < 0)
|
|
ret = -1;
|
|
else
|
|
ret = pipe_fd[0];
|
|
|
|
pa_mutex_unlock(mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int pa_autospawn_lock_acquire(bool block) {
|
|
int ret = -1;
|
|
|
|
create_mutex();
|
|
pa_mutex_lock(mutex);
|
|
pa_assert(n_ref >= 1);
|
|
|
|
pa_mutex_lock(lock_fd_mutex);
|
|
|
|
for (;;) {
|
|
|
|
empty_pipe();
|
|
|
|
if (state == STATE_OWNING) {
|
|
state = STATE_TAKEN;
|
|
ret = 1;
|
|
break;
|
|
}
|
|
|
|
if (state == STATE_FAILED) {
|
|
ret = -1;
|
|
break;
|
|
}
|
|
|
|
if (state == STATE_IDLE)
|
|
if (start_thread() < 0)
|
|
break;
|
|
|
|
if (!block) {
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
pa_mutex_unlock(lock_fd_mutex);
|
|
pa_mutex_unlock(mutex);
|
|
|
|
wait_for_ping();
|
|
|
|
pa_mutex_lock(mutex);
|
|
pa_mutex_lock(lock_fd_mutex);
|
|
}
|
|
|
|
pa_mutex_unlock(lock_fd_mutex);
|
|
|
|
pa_mutex_unlock(mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void pa_autospawn_lock_release(void) {
|
|
|
|
create_mutex();
|
|
pa_mutex_lock(mutex);
|
|
pa_assert(n_ref >= 1);
|
|
|
|
pa_assert(state == STATE_TAKEN);
|
|
state = STATE_OWNING;
|
|
|
|
ping();
|
|
|
|
pa_mutex_unlock(mutex);
|
|
}
|
|
|
|
void pa_autospawn_lock_done(bool after_fork) {
|
|
|
|
create_mutex();
|
|
pa_mutex_lock(mutex);
|
|
pa_assert(n_ref >= 1);
|
|
|
|
unref(after_fork);
|
|
|
|
pa_mutex_unlock(mutex);
|
|
}
|