diff options
author | Lennart Poettering <lennart@poettering.net> | 2008-08-09 03:49:42 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2008-08-09 03:49:42 +0200 |
commit | 15cebbacebf27703848afb18883fad7984349f04 (patch) | |
tree | 22f820a7f1cf96b4d0609fbacd044090cc3d3f28 | |
parent | b4a566918cc6fbf2d5e66ae354275d36fbe8d13a (diff) |
rework autospawning code to survive multiple pa_contexts in a single process
-rw-r--r-- | src/.gitignore | 1 | ||||
-rw-r--r-- | src/Makefile.am | 14 | ||||
-rw-r--r-- | src/daemon/main.c | 36 | ||||
-rw-r--r-- | src/pulse/context.c | 105 | ||||
-rw-r--r-- | src/pulse/internal.h | 7 | ||||
-rw-r--r-- | src/pulse/lock-autospawn.c | 329 | ||||
-rw-r--r-- | src/pulse/lock-autospawn.h | 32 | ||||
-rw-r--r-- | src/tests/lock-autospawn-test.c | 109 |
8 files changed, 595 insertions, 38 deletions
diff --git a/src/.gitignore b/src/.gitignore index 6902eb9f..543f4e8e 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,3 +1,4 @@ +lock-autospawn-test *.lo *.o *.la diff --git a/src/Makefile.am b/src/Makefile.am index 29a6ef47..21584ad9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 += \ diff --git a/src/daemon/main.c b/src/daemon/main.c index 9cdcb6cd..c6fb3c69 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -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); diff --git a/src/pulse/context.c b/src/pulse/context.c index bdd519a6..00236b96 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -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; + } + } } diff --git a/src/pulse/internal.h b/src/pulse/internal.h index bfe888ee..26fb04d4 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -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; diff --git a/src/pulse/lock-autospawn.c b/src/pulse/lock-autospawn.c new file mode 100644 index 00000000..33a53113 --- /dev/null +++ b/src/pulse/lock-autospawn.c @@ -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); +} diff --git a/src/pulse/lock-autospawn.h b/src/pulse/lock-autospawn.h new file mode 100644 index 00000000..c04c4bd1 --- /dev/null +++ b/src/pulse/lock-autospawn.h @@ -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 diff --git a/src/tests/lock-autospawn-test.c b/src/tests/lock-autospawn-test.c new file mode 100644 index 00000000..cb3dc87c --- /dev/null +++ b/src/tests/lock-autospawn-test.c @@ -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; +} |