mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-04 13:29:59 -05:00
rework autospawning code to survive multiple pa_contexts in a single process
This commit is contained in:
parent
b4a566918c
commit
15cebbaceb
8 changed files with 595 additions and 38 deletions
1
src/.gitignore
vendored
1
src/.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
lock-autospawn-test
|
||||
*.lo
|
||||
*.o
|
||||
*.la
|
||||
|
|
|
|||
|
|
@ -262,7 +262,8 @@ noinst_PROGRAMS = \
|
|||
envelope-test \
|
||||
proplist-test \
|
||||
rtstutter \
|
||||
stripnul
|
||||
stripnul \
|
||||
lock-autospawn-test
|
||||
|
||||
if HAVE_SIGXCPU
|
||||
noinst_PROGRAMS += \
|
||||
|
|
@ -452,6 +453,11 @@ stripnul_LDADD = $(AM_LDADD) libpulsecore.la
|
|||
stripnul_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
|
||||
stripnul_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
|
||||
|
||||
lock_autospawn_test_SOURCES = tests/lock-autospawn-test.c
|
||||
lock_autospawn_test_LDADD = $(AM_LDADD) libpulsecore.la
|
||||
lock_autospawn_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
|
||||
lock_autospawn_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
|
||||
|
||||
###################################
|
||||
# Client library #
|
||||
###################################
|
||||
|
|
@ -535,7 +541,8 @@ libpulse_la_SOURCES = \
|
|||
pulse/xmalloc.c pulse/xmalloc.h \
|
||||
pulse/proplist.c pulse/proplist.h \
|
||||
pulse/ext-stream-restore.c pulse/ext-stream-restore.h \
|
||||
pulse/i18n.c pulse/i18n.h
|
||||
pulse/i18n.c pulse/i18n.h \
|
||||
pulse/lock-autospawn.c pulse/lock-autospawn.h
|
||||
|
||||
# Internal stuff that is shared with libpulsecore
|
||||
libpulse_la_SOURCES += \
|
||||
|
|
@ -731,7 +738,8 @@ libpulsecore_la_SOURCES = \
|
|||
pulse/volume.c pulse/volume.h \
|
||||
pulse/xmalloc.c pulse/xmalloc.h \
|
||||
pulse/proplist.c pulse/proplist.h \
|
||||
pulse/i18n.c pulse/i18n.h
|
||||
pulse/i18n.c pulse/i18n.h \
|
||||
pulse/lock-autospawn.c pulse/lock-autospawn.h
|
||||
|
||||
# Pure core stuff (some are shared in libpulse though).
|
||||
libpulsecore_la_SOURCES += \
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@
|
|||
#include <pulse/timeval.h>
|
||||
#include <pulse/xmalloc.h>
|
||||
#include <pulse/i18n.h>
|
||||
#include <pulse/lock-autospawn.h>
|
||||
|
||||
#include <pulsecore/winsock.h>
|
||||
#include <pulsecore/core-error.h>
|
||||
|
|
@ -95,8 +96,6 @@
|
|||
#include "ltdl-bind-now.h"
|
||||
#include "polkit.h"
|
||||
|
||||
#define AUTOSPAWN_LOCK "autospawn.lock"
|
||||
|
||||
#ifdef HAVE_LIBWRAP
|
||||
/* Only one instance of these variables */
|
||||
int allow_severity = LOG_INFO;
|
||||
|
|
@ -346,7 +345,8 @@ int main(int argc, char *argv[]) {
|
|||
struct timeval win32_tv;
|
||||
#endif
|
||||
char *lf = NULL;
|
||||
int autospawn_lock_fd = -1;
|
||||
int autospawn_fd = -1;
|
||||
pa_bool_t autospawn_locked = FALSE;
|
||||
|
||||
#if defined(__linux__) && defined(__OPTIMIZE__)
|
||||
/*
|
||||
|
|
@ -656,8 +656,17 @@ int main(int argc, char *argv[]) {
|
|||
* first take the autospawn lock to make things
|
||||
* synchronous. */
|
||||
|
||||
lf = pa_runtime_path(AUTOSPAWN_LOCK);
|
||||
autospawn_lock_fd = pa_lock_lockfile(lf);
|
||||
if ((autospawn_fd = pa_autospawn_lock_init()) < 0) {
|
||||
pa_log("Failed to initialize autospawn lock");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((pa_autospawn_lock_acquire(TRUE) < 0)) {
|
||||
pa_log("Failed to acquire autospawn lock");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
autospawn_locked = TRUE;
|
||||
}
|
||||
|
||||
if (conf->daemonize) {
|
||||
|
|
@ -703,12 +712,15 @@ int main(int argc, char *argv[]) {
|
|||
goto finish;
|
||||
}
|
||||
|
||||
if (autospawn_lock_fd >= 0) {
|
||||
if (autospawn_fd >= 0) {
|
||||
/* The lock file is unlocked from the parent, so we need
|
||||
* to close it in the child */
|
||||
|
||||
pa_close(autospawn_lock_fd);
|
||||
autospawn_lock_fd = -1;
|
||||
pa_autospawn_lock_release();
|
||||
pa_autospawn_lock_done(TRUE);
|
||||
|
||||
autospawn_locked = FALSE;
|
||||
autospawn_fd = -1;
|
||||
}
|
||||
|
||||
pa_assert_se(pa_close(daemon_pipe[0]) == 0);
|
||||
|
|
@ -917,8 +929,12 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
finish:
|
||||
|
||||
if (autospawn_lock_fd >= 0)
|
||||
pa_unlock_lockfile(lf, autospawn_lock_fd);
|
||||
if (autospawn_fd >= 0) {
|
||||
if (autospawn_locked)
|
||||
pa_autospawn_lock_release();
|
||||
|
||||
pa_autospawn_lock_done(FALSE);
|
||||
}
|
||||
|
||||
if (lf)
|
||||
pa_xfree(lf);
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@
|
|||
#include <pulse/utf8.h>
|
||||
#include <pulse/util.h>
|
||||
#include <pulse/i18n.h>
|
||||
#include <pulse/lock-autospawn.h>
|
||||
|
||||
#include <pulsecore/winsock.h>
|
||||
#include <pulsecore/core-error.h>
|
||||
|
|
@ -81,8 +82,6 @@
|
|||
|
||||
#include "context.h"
|
||||
|
||||
#define AUTOSPAWN_LOCK "autospawn.lock"
|
||||
|
||||
void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
|
||||
|
||||
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
|
||||
|
|
@ -100,20 +99,23 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
|
|||
[PA_COMMAND_EXTENSION] = pa_command_extension
|
||||
};
|
||||
|
||||
static void unlock_autospawn_lock_file(pa_context *c) {
|
||||
static void unlock_autospawn(pa_context *c) {
|
||||
pa_assert(c);
|
||||
|
||||
if (c->autospawn_lock_fd >= 0) {
|
||||
char *lf;
|
||||
if (c->autospawn_fd >= 0) {
|
||||
|
||||
if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
|
||||
pa_log_warn(_("Cannot unlock autospawn because runtime path is no more."));
|
||||
if (c->autospawn_locked)
|
||||
pa_autospawn_lock_release();
|
||||
|
||||
pa_unlock_lockfile(lf, c->autospawn_lock_fd);
|
||||
pa_xfree(lf);
|
||||
if (c->autospawn_event)
|
||||
c->mainloop->io_free(c->autospawn_event);
|
||||
|
||||
c->autospawn_lock_fd = -1;
|
||||
pa_autospawn_lock_done(FALSE);
|
||||
}
|
||||
|
||||
c->autospawn_locked = FALSE;
|
||||
c->autospawn_fd = -1;
|
||||
c->autospawn_event = NULL;
|
||||
}
|
||||
|
||||
static void context_free(pa_context *c);
|
||||
|
|
@ -174,11 +176,15 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *
|
|||
c->is_local = FALSE;
|
||||
c->server_list = NULL;
|
||||
c->server = NULL;
|
||||
c->autospawn_lock_fd = -1;
|
||||
memset(&c->spawn_api, 0, sizeof(c->spawn_api));
|
||||
c->do_autospawn = FALSE;
|
||||
|
||||
c->do_shm = FALSE;
|
||||
|
||||
c->do_autospawn = FALSE;
|
||||
c->autospawn_fd = -1;
|
||||
c->autospawn_locked = FALSE;
|
||||
c->autospawn_event = NULL;
|
||||
memset(&c->spawn_api, 0, sizeof(c->spawn_api));
|
||||
|
||||
#ifndef MSG_NOSIGNAL
|
||||
#ifdef SIGPIPE
|
||||
pa_check_signal_is_blocked(SIGPIPE);
|
||||
|
|
@ -246,7 +252,7 @@ static void context_free(pa_context *c) {
|
|||
|
||||
context_unlink(c);
|
||||
|
||||
unlock_autospawn_lock_file(c);
|
||||
unlock_autospawn(c);
|
||||
|
||||
if (c->record_streams)
|
||||
pa_dynarray_free(c->record_streams, NULL, NULL);
|
||||
|
|
@ -674,7 +680,7 @@ static int context_connect_spawn(pa_context *c) {
|
|||
|
||||
c->is_local = TRUE;
|
||||
|
||||
unlock_autospawn_lock_file(c);
|
||||
unlock_autospawn(c);
|
||||
|
||||
io = pa_iochannel_new(c->mainloop, fds[0], fds[0]);
|
||||
setup_context(c, io);
|
||||
|
|
@ -686,7 +692,7 @@ static int context_connect_spawn(pa_context *c) {
|
|||
fail:
|
||||
pa_close_pipe(fds);
|
||||
|
||||
unlock_autospawn_lock_file(c);
|
||||
unlock_autospawn(c);
|
||||
|
||||
pa_context_unref(c);
|
||||
|
||||
|
|
@ -768,13 +774,46 @@ static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userd
|
|||
goto finish;
|
||||
}
|
||||
|
||||
unlock_autospawn_lock_file(c);
|
||||
unlock_autospawn(c);
|
||||
setup_context(c, io);
|
||||
|
||||
finish:
|
||||
pa_context_unref(c);
|
||||
}
|
||||
|
||||
static void autospawn_cb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
|
||||
pa_context *c = userdata;
|
||||
int k;
|
||||
|
||||
pa_assert(a);
|
||||
pa_assert(e);
|
||||
pa_assert(fd >= 0);
|
||||
pa_assert(events = PA_IO_EVENT_INPUT);
|
||||
pa_assert(c);
|
||||
pa_assert(e == c->autospawn_event);
|
||||
pa_assert(fd == c->autospawn_fd);
|
||||
|
||||
pa_context_ref(c);
|
||||
|
||||
/* Check whether we can get the lock right now*/
|
||||
if ((k = pa_autospawn_lock_acquire(FALSE)) < 0) {
|
||||
pa_context_fail(c, PA_ERR_ACCESS);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (k > 0) {
|
||||
/* So we got it, rock on! */
|
||||
c->autospawn_locked = TRUE;
|
||||
try_next_connection(c);
|
||||
|
||||
c->mainloop->io_free(c->autospawn_event);
|
||||
c->autospawn_event = NULL;
|
||||
}
|
||||
|
||||
finish:
|
||||
|
||||
pa_context_unref(c);
|
||||
}
|
||||
|
||||
static char *get_old_legacy_runtime_dir(void) {
|
||||
char *p, u[128];
|
||||
|
|
@ -847,6 +886,7 @@ int pa_context_connect(
|
|||
pa_context_fail(c, PA_ERR_INVALIDSERVER);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
} else {
|
||||
char *d, *ufn;
|
||||
static char *legacy_dir;
|
||||
|
|
@ -895,21 +935,40 @@ int pa_context_connect(
|
|||
|
||||
/* Wrap the connection attempts in a single transaction for sane autospawn locking */
|
||||
if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) {
|
||||
char *lf;
|
||||
int k;
|
||||
|
||||
if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
|
||||
pa_assert(c->autospawn_fd < 0);
|
||||
pa_assert(!c->autospawn_locked);
|
||||
|
||||
/* Start the locking procedure */
|
||||
if ((c->autospawn_fd = pa_autospawn_lock_init()) < 0) {
|
||||
pa_context_fail(c, PA_ERR_ACCESS);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
pa_assert(c->autospawn_lock_fd <= 0);
|
||||
c->autospawn_lock_fd = pa_lock_lockfile(lf);
|
||||
pa_xfree(lf);
|
||||
|
||||
if (api)
|
||||
c->spawn_api = *api;
|
||||
|
||||
c->do_autospawn = TRUE;
|
||||
|
||||
/* Check whether we can get the lock right now*/
|
||||
if ((k = pa_autospawn_lock_acquire(FALSE)) < 0) {
|
||||
pa_context_fail(c, PA_ERR_ACCESS);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (k > 0)
|
||||
/* So we got it, rock on! */
|
||||
c->autospawn_locked = TRUE;
|
||||
else {
|
||||
/* Hmm, we didn't get it, so let's wait for it */
|
||||
c->autospawn_event = c->mainloop->io_new(c->mainloop, c->autospawn_fd, PA_IO_EVENT_INPUT, autospawn_cb, c);
|
||||
|
||||
pa_context_set_state(c, PA_CONTEXT_CONNECTING);
|
||||
r = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -75,9 +75,12 @@ struct pa_context {
|
|||
pa_mempool *mempool;
|
||||
|
||||
pa_bool_t is_local:1;
|
||||
pa_bool_t do_autospawn:1;
|
||||
pa_bool_t do_shm:1;
|
||||
int autospawn_lock_fd;
|
||||
|
||||
pa_bool_t do_autospawn:1;
|
||||
pa_bool_t autospawn_locked:1;
|
||||
int autospawn_fd;
|
||||
pa_io_event *autospawn_event;
|
||||
pa_spawn_api spawn_api;
|
||||
|
||||
pa_strlist *server_list;
|
||||
|
|
|
|||
329
src/pulse/lock-autospawn.c
Normal file
329
src/pulse/lock-autospawn.c
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
/***
|
||||
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 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, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/poll.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <pulse/i18n.h>
|
||||
#include <pulse/xmalloc.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_bool_t taken = FALSE;
|
||||
static pa_thread *thread;
|
||||
static int pipe_fd[2] = { -1, -1 };
|
||||
|
||||
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);
|
||||
|
||||
n_ref++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
pa_assert(lock_fd < 0);
|
||||
pa_assert(!lock_fd_mutex);
|
||||
pa_assert(!taken);
|
||||
pa_assert(!thread);
|
||||
pa_assert(pipe_fd[0] < 0);
|
||||
pa_assert(pipe_fd[1] < 0);
|
||||
|
||||
if (pipe(pipe_fd) < 0)
|
||||
return -1;
|
||||
|
||||
lock_fd_mutex = pa_mutex_new(FALSE, FALSE);
|
||||
|
||||
pa_make_fd_cloexec(pipe_fd[0]);
|
||||
pa_make_fd_cloexec(pipe_fd[1]);
|
||||
|
||||
pa_make_fd_nonblock(pipe_fd[1]);
|
||||
pa_make_fd_nonblock(pipe_fd[0]);
|
||||
|
||||
n_ref = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unref(pa_bool_t 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;
|
||||
|
||||
pa_assert(!taken);
|
||||
|
||||
if (thread) {
|
||||
pa_thread_free(thread);
|
||||
thread = NULL;
|
||||
}
|
||||
|
||||
pa_mutex_lock(lock_fd_mutex);
|
||||
if (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;
|
||||
}
|
||||
}
|
||||
pa_mutex_unlock(lock_fd_mutex);
|
||||
|
||||
pa_mutex_free(lock_fd_mutex);
|
||||
|
||||
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 = write(pipe_fd[1], &x, 1)) == 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 = poll(&pfd, 1, -1)) != 1) {
|
||||
pa_assert(k < 0);
|
||||
pa_assert(errno == EINTR);
|
||||
} else if ((s = read(pipe_fd[0], &x, 1)) != 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 = read(pipe_fd[0], &x, sizeof(x))) < 1) {
|
||||
pa_assert(s < 0);
|
||||
pa_assert(errno == EAGAIN);
|
||||
}
|
||||
}
|
||||
|
||||
static void thread_func(void *u) {
|
||||
int fd;
|
||||
char *lf;
|
||||
sigset_t fullset;
|
||||
|
||||
/* No signals in this thread please */
|
||||
sigfillset(&fullset);
|
||||
pthread_sigmask(SIG_BLOCK, &fullset, NULL);
|
||||
|
||||
if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
|
||||
pa_log_warn(_("Cannot access autospawn lock."));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((fd = pa_lock_lockfile(lf)) < 0)
|
||||
goto finish;
|
||||
|
||||
pa_mutex_lock(lock_fd_mutex);
|
||||
pa_assert(lock_fd < 0);
|
||||
lock_fd = fd;
|
||||
pa_mutex_unlock(lock_fd_mutex);
|
||||
|
||||
finish:
|
||||
pa_xfree(lf);
|
||||
|
||||
ping();
|
||||
}
|
||||
|
||||
static int start_thread(void) {
|
||||
|
||||
if (!thread)
|
||||
if (!(thread = pa_thread_new(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(pa_bool_t 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 (lock_fd >= 0 && !taken) {
|
||||
taken = TRUE;
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (lock_fd < 0)
|
||||
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(taken);
|
||||
taken = FALSE;
|
||||
|
||||
ping();
|
||||
|
||||
pa_mutex_unlock(mutex);
|
||||
}
|
||||
|
||||
void pa_autospawn_lock_done(pa_bool_t after_fork) {
|
||||
|
||||
create_mutex();
|
||||
pa_mutex_lock(mutex);
|
||||
pa_assert(n_ref >= 1);
|
||||
|
||||
unref(after_fork);
|
||||
|
||||
pa_mutex_unlock(mutex);
|
||||
}
|
||||
32
src/pulse/lock-autospawn.h
Normal file
32
src/pulse/lock-autospawn.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef foopulselockautospawnhfoo
|
||||
#define foopulselockautospawnhfoo
|
||||
|
||||
/***
|
||||
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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with PulseAudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#include <pulsecore/macro.h>
|
||||
|
||||
int pa_autospawn_lock_init(void);
|
||||
int pa_autospawn_lock_acquire(pa_bool_t block);
|
||||
void pa_autospawn_lock_release(void);
|
||||
void pa_autospawn_lock_done(pa_bool_t after_fork);
|
||||
|
||||
#endif
|
||||
109
src/tests/lock-autospawn-test.c
Normal file
109
src/tests/lock-autospawn-test.c
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/***
|
||||
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 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, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/poll.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <pulsecore/macro.h>
|
||||
#include <pulsecore/thread.h>
|
||||
#include <pulse/lock-autospawn.h>
|
||||
#include <pulse/util.h>
|
||||
|
||||
static void thread_func(void*k) {
|
||||
pa_assert_se(pa_autospawn_lock_init() >= 0);
|
||||
|
||||
pa_log("%i, Trying to acquire lock.", PA_PTR_TO_INT(k));
|
||||
|
||||
pa_assert_se(pa_autospawn_lock_acquire(TRUE) > 0);
|
||||
|
||||
pa_log("%i, Got the lock!, Sleeping for 5s", PA_PTR_TO_INT(k));
|
||||
|
||||
pa_msleep(5000);
|
||||
|
||||
pa_log("%i, Releasing", PA_PTR_TO_INT(k));
|
||||
|
||||
pa_autospawn_lock_release();
|
||||
|
||||
pa_autospawn_lock_done(FALSE);
|
||||
}
|
||||
|
||||
static void thread_func2(void *k) {
|
||||
int fd;
|
||||
|
||||
pa_assert_se((fd = pa_autospawn_lock_init()) >= 0);
|
||||
|
||||
pa_log("%i, Trying to acquire lock.", PA_PTR_TO_INT(k));
|
||||
|
||||
for (;;) {
|
||||
struct pollfd pollfd;
|
||||
int j;
|
||||
|
||||
if ((j = pa_autospawn_lock_acquire(FALSE)) > 0)
|
||||
break;
|
||||
|
||||
pa_assert(j == 0);
|
||||
|
||||
memset(&pollfd, 0, sizeof(pollfd));
|
||||
pollfd.fd = fd;
|
||||
pollfd.events = POLLIN;
|
||||
|
||||
pa_assert_se(poll(&pollfd, 1, -1) == 1);
|
||||
|
||||
pa_log("%i, woke up", PA_PTR_TO_INT(k));
|
||||
}
|
||||
|
||||
pa_log("%i, Got the lock!, Sleeping for 5s", PA_PTR_TO_INT(k));
|
||||
|
||||
pa_msleep(5000);
|
||||
|
||||
pa_log("%i, Releasing", PA_PTR_TO_INT(k));
|
||||
|
||||
pa_autospawn_lock_release();
|
||||
|
||||
pa_autospawn_lock_done(FALSE);
|
||||
}
|
||||
|
||||
int main(int argc, char**argv) {
|
||||
pa_thread *a, *b, *c, *d;
|
||||
|
||||
pa_assert_se((a = pa_thread_new(thread_func, PA_INT_TO_PTR(1))));
|
||||
pa_assert_se((b = pa_thread_new(thread_func2, PA_INT_TO_PTR(2))));
|
||||
pa_assert_se((c = pa_thread_new(thread_func2, PA_INT_TO_PTR(3))));
|
||||
pa_assert_se((d = pa_thread_new(thread_func, PA_INT_TO_PTR(4))));
|
||||
|
||||
pa_thread_join(a);
|
||||
pa_thread_join(b);
|
||||
pa_thread_join(c);
|
||||
pa_thread_join(d);
|
||||
|
||||
pa_thread_free(a);
|
||||
pa_thread_free(b);
|
||||
pa_thread_free(c);
|
||||
pa_thread_free(d);
|
||||
|
||||
pa_log("End");
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue