rework autospawning code to survive multiple pa_contexts in a single process

This commit is contained in:
Lennart Poettering 2008-08-09 03:49:42 +02:00
parent b4a566918c
commit 15cebbaceb
8 changed files with 595 additions and 38 deletions

1
src/.gitignore vendored
View file

@ -1,3 +1,4 @@
lock-autospawn-test
*.lo *.lo
*.o *.o
*.la *.la

View file

@ -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 += \

View file

@ -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);

View file

@ -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;
}
} }
} }

View file

@ -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
View 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);
}

View 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

View 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;
}