diff options
| -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; +}  | 
