From b2c341f935bd54eb1b7f80a297e72bf0e6c6dc83 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 29 Aug 2006 19:51:14 +0000 Subject: add a threading primitive API git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1344 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 16 ++++- src/pulsecore/mutex-posix.c | 121 +++++++++++++++++++++++++++++++++++ src/pulsecore/mutex.h | 39 ++++++++++++ src/pulsecore/thread-posix.c | 146 +++++++++++++++++++++++++++++++++++++++++++ src/pulsecore/thread.h | 44 +++++++++++++ src/tests/thread-test.c | 135 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 498 insertions(+), 3 deletions(-) create mode 100644 src/pulsecore/mutex-posix.c create mode 100644 src/pulsecore/mutex.h create mode 100644 src/pulsecore/thread-posix.c create mode 100644 src/pulsecore/thread.h create mode 100644 src/tests/thread-test.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index c9942cff..bbdc4612 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -198,7 +198,8 @@ noinst_PROGRAMS = \ get-binary-name-test \ ipacl-test \ hook-list-test \ - memblock-test + memblock-test \ + thread-test if HAVE_SIGXCPU noinst_PROGRAMS += \ @@ -248,6 +249,11 @@ memblock_test_CFLAGS = $(AM_CFLAGS) memblock_test_LDADD = $(AM_LDADD) libpulsecore.la memblock_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) +thread_test_SOURCES = tests/thread-test.c +thread_test_CFLAGS = $(AM_CFLAGS) +thread_test_LDADD = $(AM_LDADD) libpulsecore.la +thread_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + mcalign_test_SOURCES = tests/mcalign-test.c mcalign_test_CFLAGS = $(AM_CFLAGS) mcalign_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la @@ -529,7 +535,9 @@ pulsecoreinclude_HEADERS = \ pulsecore/creds.h \ pulsecore/shm.h \ pulsecore/llist.h \ - pulsecore/refcnt.h + pulsecore/refcnt.h \ + pulsecore/mutex.h \ + pulsecore/thread.h lib_LTLIBRARIES += libpulsecore.la @@ -597,7 +605,9 @@ libpulsecore_la_SOURCES += \ pulsecore/winsock.h \ pulsecore/core-error.c pulsecore/core-error.h \ pulsecore/hook-list.c pulsecore/hook-list.h \ - pulsecore/shm.c pulsecore/shm.h + pulsecore/shm.c pulsecore/shm.h \ + pulsecore/mutex-posix.c pulsecore/mutex.h \ + pulsecore/thread-posix.c pulsecore/thread.h if OS_IS_WIN32 libpulsecore_la_SOURCES += \ diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c new file mode 100644 index 00000000..d0156dbf --- /dev/null +++ b/src/pulsecore/mutex-posix.c @@ -0,0 +1,121 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 +#endif + +#include +#include +#include + +#include + +#include "mutex.h" + +#define ASSERT_SUCCESS(x) do { \ + int _r = (x); \ + assert(_r == 0); \ +} while(0) + +struct pa_mutex { + pthread_mutex_t mutex; +}; + +struct pa_cond { + pthread_cond_t cond; +}; + +pa_mutex* pa_mutex_new(int recursive) { + pa_mutex *m; + pthread_mutexattr_t attr; + + pthread_mutexattr_init(&attr); + + if (recursive) + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) < 0) + return NULL; + + m = pa_xnew(pa_mutex, 1); + + if (pthread_mutex_init(&m->mutex, &attr) < 0) { + pa_xfree(m); + return NULL; + } + + return m; +} + +void pa_mutex_free(pa_mutex *m) { + assert(m); + + ASSERT_SUCCESS(pthread_mutex_destroy(&m->mutex)); + pa_xfree(m); +} + +void pa_mutex_lock(pa_mutex *m) { + assert(m); + + ASSERT_SUCCESS(pthread_mutex_lock(&m->mutex)); +} + +void pa_mutex_unlock(pa_mutex *m) { + assert(m); + + ASSERT_SUCCESS(pthread_mutex_unlock(&m->mutex)); +} + + +pa_cond *pa_cond_new(void) { + pa_cond *c; + + c = pa_xnew(pa_cond, 1); + + if (pthread_cond_init(&c->cond, NULL) < 0) { + pa_xfree(c); + return NULL; + } + + return c; +} + +void pa_cond_free(pa_cond *c) { + assert(c); + + ASSERT_SUCCESS(pthread_cond_destroy(&c->cond)); + pa_xfree(c); +} + +void pa_cond_signal(pa_cond *c, int broadcast) { + assert(c); + + if (broadcast) + ASSERT_SUCCESS(pthread_cond_broadcast(&c->cond)); + else + ASSERT_SUCCESS(pthread_cond_signal(&c->cond)); +} + +int pa_cond_wait(pa_cond *c, pa_mutex *m) { + assert(c); + assert(m); + + return pthread_cond_wait(&c->cond, &m->mutex); +} diff --git a/src/pulsecore/mutex.h b/src/pulsecore/mutex.h new file mode 100644 index 00000000..b3b9c5c6 --- /dev/null +++ b/src/pulsecore/mutex.h @@ -0,0 +1,39 @@ +#ifndef foopulsemutexhfoo +#define foopulsemutexhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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. +***/ + +typedef struct pa_mutex pa_mutex; + +pa_mutex* pa_mutex_new(int recursive); +void pa_mutex_free(pa_mutex *m); +void pa_mutex_lock(pa_mutex *m); +void pa_mutex_unlock(pa_mutex *m); + +typedef struct pa_cond pa_cond; + +pa_cond *pa_cond_new(void); +void pa_cond_free(pa_cond *c); +void pa_cond_signal(pa_cond *c, int broadcast); +int pa_cond_wait(pa_cond *c, pa_mutex *m); + +#endif diff --git a/src/pulsecore/thread-posix.c b/src/pulsecore/thread-posix.c new file mode 100644 index 00000000..15ade290 --- /dev/null +++ b/src/pulsecore/thread-posix.c @@ -0,0 +1,146 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 +#endif + +#include +#include +#include + +#include + +#include "thread.h" + +#define ASSERT_SUCCESS(x) do { \ + int _r = (x); \ + assert(_r == 0); \ +} while(0) + +struct pa_thread { + pthread_t id; + pa_thread_func_t thread_func; + void *userdata; + AO_t running; +}; + +struct pa_tls { + pthread_key_t key; +}; + +static pa_tls *thread_tls; +static pthread_once_t thread_tls_once = PTHREAD_ONCE_INIT; + +static void thread_tls_once_func(void) { + thread_tls = pa_tls_new(NULL); + assert(thread_tls); +} + +static void* internal_thread_func(void *userdata) { + pa_thread *t = userdata; + assert(t); + + t->id = pthread_self(); + + ASSERT_SUCCESS(pthread_once(&thread_tls_once, thread_tls_once_func)); + pa_tls_set(thread_tls, t); + + AO_store_release_write(&t->running, 1); + t->thread_func(t->userdata); + AO_store_release_write(&t->running, 0); + + return NULL; +} + +pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata) { + pa_thread *t; + + t = pa_xnew(pa_thread, 1); + t->thread_func = thread_func; + t->userdata = userdata; + + if (pthread_create(&t->id, NULL, internal_thread_func, t) < 0) { + pa_xfree(t); + return NULL; + } + + return t; +} + +int pa_thread_is_running(pa_thread *t) { + assert(t); + + return !!AO_load_acquire_read(&t->running); +} + +void pa_thread_free(pa_thread *t) { + assert(t); + + pa_thread_join(t); + pa_xfree(t); +} + +int pa_thread_join(pa_thread *t) { + assert(t); + + return pthread_join(t->id, NULL); +} + +pa_thread* pa_thread_self(void) { + ASSERT_SUCCESS(pthread_once(&thread_tls_once, thread_tls_once_func)); + return pa_tls_get(thread_tls); +} + +pa_tls* pa_tls_new(pa_free_cb_t free_cb) { + pa_tls *t; + + t = pa_xnew(pa_tls, 1); + + if (pthread_key_create(&t->key, free_cb) < 0) { + pa_xfree(t); + return NULL; + } + + return t; +} + +void pa_tls_free(pa_tls *t) { + assert(t); + + ASSERT_SUCCESS(pthread_key_delete(t->key)); + pa_xfree(t); +} + +void *pa_tls_get(pa_tls *t) { + assert(t); + + return pthread_getspecific(t->key); +} + +void *pa_tls_set(pa_tls *t, void *userdata) { + void *r; + + r = pthread_getspecific(t->key); + ASSERT_SUCCESS(pthread_setspecific(t->key, userdata)); + return r; +} + diff --git a/src/pulsecore/thread.h b/src/pulsecore/thread.h new file mode 100644 index 00000000..978e9927 --- /dev/null +++ b/src/pulsecore/thread.h @@ -0,0 +1,44 @@ +#ifndef foopulsethreadhfoo +#define foopulsethreadhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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. +***/ + +#include + +typedef struct pa_thread pa_thread; + +typedef void (*pa_thread_func_t) (void *userdata); + +pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata); +void pa_thread_free(pa_thread *t); +int pa_thread_join(pa_thread *t); +int pa_thread_is_running(pa_thread *t); +pa_thread *pa_thread_self(void); + +typedef struct pa_tls pa_tls; + +pa_tls* pa_tls_new(pa_free_cb_t free_cb); +void pa_tls_free(pa_tls *t); +void * pa_tls_get(pa_tls *t); +void *pa_tls_set(pa_tls *t, void *userdata); + +#endif diff --git a/src/tests/thread-test.c b/src/tests/thread-test.c new file mode 100644 index 00000000..a93ac1e4 --- /dev/null +++ b/src/tests/thread-test.c @@ -0,0 +1,135 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + 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 +#endif + +#include + +#include +#include +#include +#include +#include + +static pa_mutex *mutex = NULL; +static pa_cond *cond1 = NULL, *cond2 = NULL; +static pa_tls *tls = NULL; + +static int magic_number = 0; + +#define THREADS_MAX 20 + +static void thread_func(void *data) { + pa_tls_set(tls, data); + + pa_log("thread_func() for %s starting...", (char*) pa_tls_get(tls)); + + pa_mutex_lock(mutex); + + for (;;) { + int k, n; + + pa_log("%s waiting ...", (char*) pa_tls_get(tls)); + + for (;;) { + + if (magic_number < 0) + goto quit; + + if (magic_number != 0) + break; + + pa_cond_wait(cond1, mutex); + } + + k = magic_number; + magic_number = 0; + + pa_mutex_unlock(mutex); + + pa_cond_signal(cond2, 0); + + pa_log("%s got number %i", (char*) pa_tls_get(tls), k); + + /* Spin! */ + for (n = 0; n < k; n++) + sched_yield(); + + pa_mutex_lock(mutex); + } + +quit: + + pa_mutex_unlock(mutex); + + pa_log("thread_func() for %s done...", (char*) pa_tls_get(tls)); +} + +int main(int argc, char *argv[]) { + int i, k; + pa_thread* t[THREADS_MAX]; + + mutex = pa_mutex_new(0); + cond1 = pa_cond_new(); + cond2 = pa_cond_new(); + tls = pa_tls_new(pa_xfree); + + for (i = 0; i < THREADS_MAX; i++) { + t[i] = pa_thread_new(thread_func, pa_sprintf_malloc("Thread #%i", i+1)); + assert(t[i]); + } + + pa_mutex_lock(mutex); + + pa_log("loop-init"); + + for (k = 0; k < 100; k++) { + assert(magic_number == 0); + + + magic_number = (int) rand() % 0x10000; + + pa_log("iteration %i (%i)", k, magic_number); + + pa_cond_signal(cond1, 0); + + pa_cond_wait(cond2, mutex); + } + + pa_log("loop-exit"); + + magic_number = -1; + pa_cond_signal(cond1, 1); + + pa_mutex_unlock(mutex); + + for (i = 0; i < THREADS_MAX; i++) + pa_thread_free(t[i]); + + pa_mutex_free(mutex); + pa_cond_free(cond1); + pa_cond_free(cond2); + pa_tls_free(tls); + + return 0; +} -- cgit