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
|
*.lo
|
||||||
*.o
|
*.o
|
||||||
*.la
|
*.la
|
||||||
|
|
|
||||||
|
|
@ -262,7 +262,8 @@ noinst_PROGRAMS = \
|
||||||
envelope-test \
|
envelope-test \
|
||||||
proplist-test \
|
proplist-test \
|
||||||
rtstutter \
|
rtstutter \
|
||||||
stripnul
|
stripnul \
|
||||||
|
lock-autospawn-test
|
||||||
|
|
||||||
if HAVE_SIGXCPU
|
if HAVE_SIGXCPU
|
||||||
noinst_PROGRAMS += \
|
noinst_PROGRAMS += \
|
||||||
|
|
@ -452,6 +453,11 @@ stripnul_LDADD = $(AM_LDADD) libpulsecore.la
|
||||||
stripnul_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
|
stripnul_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
|
||||||
stripnul_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
|
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 #
|
# Client library #
|
||||||
###################################
|
###################################
|
||||||
|
|
@ -535,7 +541,8 @@ libpulse_la_SOURCES = \
|
||||||
pulse/xmalloc.c pulse/xmalloc.h \
|
pulse/xmalloc.c pulse/xmalloc.h \
|
||||||
pulse/proplist.c pulse/proplist.h \
|
pulse/proplist.c pulse/proplist.h \
|
||||||
pulse/ext-stream-restore.c pulse/ext-stream-restore.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
|
# Internal stuff that is shared with libpulsecore
|
||||||
libpulse_la_SOURCES += \
|
libpulse_la_SOURCES += \
|
||||||
|
|
@ -731,7 +738,8 @@ libpulsecore_la_SOURCES = \
|
||||||
pulse/volume.c pulse/volume.h \
|
pulse/volume.c pulse/volume.h \
|
||||||
pulse/xmalloc.c pulse/xmalloc.h \
|
pulse/xmalloc.c pulse/xmalloc.h \
|
||||||
pulse/proplist.c pulse/proplist.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).
|
# Pure core stuff (some are shared in libpulse though).
|
||||||
libpulsecore_la_SOURCES += \
|
libpulsecore_la_SOURCES += \
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@
|
||||||
#include <pulse/timeval.h>
|
#include <pulse/timeval.h>
|
||||||
#include <pulse/xmalloc.h>
|
#include <pulse/xmalloc.h>
|
||||||
#include <pulse/i18n.h>
|
#include <pulse/i18n.h>
|
||||||
|
#include <pulse/lock-autospawn.h>
|
||||||
|
|
||||||
#include <pulsecore/winsock.h>
|
#include <pulsecore/winsock.h>
|
||||||
#include <pulsecore/core-error.h>
|
#include <pulsecore/core-error.h>
|
||||||
|
|
@ -95,8 +96,6 @@
|
||||||
#include "ltdl-bind-now.h"
|
#include "ltdl-bind-now.h"
|
||||||
#include "polkit.h"
|
#include "polkit.h"
|
||||||
|
|
||||||
#define AUTOSPAWN_LOCK "autospawn.lock"
|
|
||||||
|
|
||||||
#ifdef HAVE_LIBWRAP
|
#ifdef HAVE_LIBWRAP
|
||||||
/* Only one instance of these variables */
|
/* Only one instance of these variables */
|
||||||
int allow_severity = LOG_INFO;
|
int allow_severity = LOG_INFO;
|
||||||
|
|
@ -346,7 +345,8 @@ int main(int argc, char *argv[]) {
|
||||||
struct timeval win32_tv;
|
struct timeval win32_tv;
|
||||||
#endif
|
#endif
|
||||||
char *lf = NULL;
|
char *lf = NULL;
|
||||||
int autospawn_lock_fd = -1;
|
int autospawn_fd = -1;
|
||||||
|
pa_bool_t autospawn_locked = FALSE;
|
||||||
|
|
||||||
#if defined(__linux__) && defined(__OPTIMIZE__)
|
#if defined(__linux__) && defined(__OPTIMIZE__)
|
||||||
/*
|
/*
|
||||||
|
|
@ -656,8 +656,17 @@ int main(int argc, char *argv[]) {
|
||||||
* first take the autospawn lock to make things
|
* first take the autospawn lock to make things
|
||||||
* synchronous. */
|
* synchronous. */
|
||||||
|
|
||||||
lf = pa_runtime_path(AUTOSPAWN_LOCK);
|
if ((autospawn_fd = pa_autospawn_lock_init()) < 0) {
|
||||||
autospawn_lock_fd = pa_lock_lockfile(lf);
|
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) {
|
if (conf->daemonize) {
|
||||||
|
|
@ -703,12 +712,15 @@ int main(int argc, char *argv[]) {
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (autospawn_lock_fd >= 0) {
|
if (autospawn_fd >= 0) {
|
||||||
/* The lock file is unlocked from the parent, so we need
|
/* The lock file is unlocked from the parent, so we need
|
||||||
* to close it in the child */
|
* to close it in the child */
|
||||||
|
|
||||||
pa_close(autospawn_lock_fd);
|
pa_autospawn_lock_release();
|
||||||
autospawn_lock_fd = -1;
|
pa_autospawn_lock_done(TRUE);
|
||||||
|
|
||||||
|
autospawn_locked = FALSE;
|
||||||
|
autospawn_fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_assert_se(pa_close(daemon_pipe[0]) == 0);
|
pa_assert_se(pa_close(daemon_pipe[0]) == 0);
|
||||||
|
|
@ -917,8 +929,12 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
|
|
||||||
if (autospawn_lock_fd >= 0)
|
if (autospawn_fd >= 0) {
|
||||||
pa_unlock_lockfile(lf, autospawn_lock_fd);
|
if (autospawn_locked)
|
||||||
|
pa_autospawn_lock_release();
|
||||||
|
|
||||||
|
pa_autospawn_lock_done(FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
if (lf)
|
if (lf)
|
||||||
pa_xfree(lf);
|
pa_xfree(lf);
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@
|
||||||
#include <pulse/utf8.h>
|
#include <pulse/utf8.h>
|
||||||
#include <pulse/util.h>
|
#include <pulse/util.h>
|
||||||
#include <pulse/i18n.h>
|
#include <pulse/i18n.h>
|
||||||
|
#include <pulse/lock-autospawn.h>
|
||||||
|
|
||||||
#include <pulsecore/winsock.h>
|
#include <pulsecore/winsock.h>
|
||||||
#include <pulsecore/core-error.h>
|
#include <pulsecore/core-error.h>
|
||||||
|
|
@ -81,8 +82,6 @@
|
||||||
|
|
||||||
#include "context.h"
|
#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);
|
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] = {
|
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
|
[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);
|
pa_assert(c);
|
||||||
|
|
||||||
if (c->autospawn_lock_fd >= 0) {
|
if (c->autospawn_fd >= 0) {
|
||||||
char *lf;
|
|
||||||
|
|
||||||
if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
|
if (c->autospawn_locked)
|
||||||
pa_log_warn(_("Cannot unlock autospawn because runtime path is no more."));
|
pa_autospawn_lock_release();
|
||||||
|
|
||||||
pa_unlock_lockfile(lf, c->autospawn_lock_fd);
|
if (c->autospawn_event)
|
||||||
pa_xfree(lf);
|
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);
|
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->is_local = FALSE;
|
||||||
c->server_list = NULL;
|
c->server_list = NULL;
|
||||||
c->server = 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_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
|
#ifndef MSG_NOSIGNAL
|
||||||
#ifdef SIGPIPE
|
#ifdef SIGPIPE
|
||||||
pa_check_signal_is_blocked(SIGPIPE);
|
pa_check_signal_is_blocked(SIGPIPE);
|
||||||
|
|
@ -246,7 +252,7 @@ static void context_free(pa_context *c) {
|
||||||
|
|
||||||
context_unlink(c);
|
context_unlink(c);
|
||||||
|
|
||||||
unlock_autospawn_lock_file(c);
|
unlock_autospawn(c);
|
||||||
|
|
||||||
if (c->record_streams)
|
if (c->record_streams)
|
||||||
pa_dynarray_free(c->record_streams, NULL, NULL);
|
pa_dynarray_free(c->record_streams, NULL, NULL);
|
||||||
|
|
@ -674,7 +680,7 @@ static int context_connect_spawn(pa_context *c) {
|
||||||
|
|
||||||
c->is_local = TRUE;
|
c->is_local = TRUE;
|
||||||
|
|
||||||
unlock_autospawn_lock_file(c);
|
unlock_autospawn(c);
|
||||||
|
|
||||||
io = pa_iochannel_new(c->mainloop, fds[0], fds[0]);
|
io = pa_iochannel_new(c->mainloop, fds[0], fds[0]);
|
||||||
setup_context(c, io);
|
setup_context(c, io);
|
||||||
|
|
@ -686,7 +692,7 @@ static int context_connect_spawn(pa_context *c) {
|
||||||
fail:
|
fail:
|
||||||
pa_close_pipe(fds);
|
pa_close_pipe(fds);
|
||||||
|
|
||||||
unlock_autospawn_lock_file(c);
|
unlock_autospawn(c);
|
||||||
|
|
||||||
pa_context_unref(c);
|
pa_context_unref(c);
|
||||||
|
|
||||||
|
|
@ -768,13 +774,46 @@ static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userd
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock_autospawn_lock_file(c);
|
unlock_autospawn(c);
|
||||||
setup_context(c, io);
|
setup_context(c, io);
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
pa_context_unref(c);
|
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) {
|
static char *get_old_legacy_runtime_dir(void) {
|
||||||
char *p, u[128];
|
char *p, u[128];
|
||||||
|
|
@ -847,6 +886,7 @@ int pa_context_connect(
|
||||||
pa_context_fail(c, PA_ERR_INVALIDSERVER);
|
pa_context_fail(c, PA_ERR_INVALIDSERVER);
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
char *d, *ufn;
|
char *d, *ufn;
|
||||||
static char *legacy_dir;
|
static char *legacy_dir;
|
||||||
|
|
@ -895,21 +935,40 @@ int pa_context_connect(
|
||||||
|
|
||||||
/* Wrap the connection attempts in a single transaction for sane autospawn locking */
|
/* Wrap the connection attempts in a single transaction for sane autospawn locking */
|
||||||
if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) {
|
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);
|
pa_context_fail(c, PA_ERR_ACCESS);
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_assert(c->autospawn_lock_fd <= 0);
|
|
||||||
c->autospawn_lock_fd = pa_lock_lockfile(lf);
|
|
||||||
pa_xfree(lf);
|
|
||||||
|
|
||||||
if (api)
|
if (api)
|
||||||
c->spawn_api = *api;
|
c->spawn_api = *api;
|
||||||
|
|
||||||
c->do_autospawn = TRUE;
|
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_mempool *mempool;
|
||||||
|
|
||||||
pa_bool_t is_local:1;
|
pa_bool_t is_local:1;
|
||||||
pa_bool_t do_autospawn:1;
|
|
||||||
pa_bool_t do_shm: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_spawn_api spawn_api;
|
||||||
|
|
||||||
pa_strlist *server_list;
|
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