summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am1
-rw-r--r--src/pulsecore/aupdate.c129
-rw-r--r--src/pulsecore/aupdate.h98
-rw-r--r--src/pulsecore/memtrap.c116
-rw-r--r--src/pulsecore/memtrap.h2
5 files changed, 264 insertions, 82 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index b6db8154..68b42dfc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -583,6 +583,7 @@ libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES = \
pulsecore/poll.c pulsecore/poll.h \
pulsecore/prioq.c pulsecore/prioq.h \
pulsecore/memtrap.c pulsecore/memtrap.h \
+ pulsecore/aupdate.c pulsecore/aupdate.h \
pulsecore/proplist-util.c pulsecore/proplist-util.h \
pulsecore/pstream-util.c pulsecore/pstream-util.h \
pulsecore/pstream.c pulsecore/pstream.h \
diff --git a/src/pulsecore/aupdate.c b/src/pulsecore/aupdate.c
new file mode 100644
index 00000000..56ebb8e5
--- /dev/null
+++ b/src/pulsecore/aupdate.c
@@ -0,0 +1,129 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 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
+ 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 <pulse/xmalloc.h>
+
+#include <pulsecore/semaphore.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/mutex.h>
+
+#include "aupdate.h"
+
+#define MSB (1U << (sizeof(unsigned)*8U-1))
+#define WHICH(n) (!!((n) & MSB))
+#define COUNTER(n) ((n) & ~MSB)
+
+struct pa_aupdate {
+ pa_atomic_t read_lock;
+ pa_mutex *write_lock;
+ pa_semaphore *semaphore;
+};
+
+pa_aupdate *pa_aupdate_new(void) {
+ pa_aupdate *a;
+
+ a = pa_xnew(pa_aupdate, 1);
+ pa_atomic_store(&a->read_lock, 0);
+ a->write_lock = pa_mutex_new(FALSE, FALSE);
+ a->semaphore = pa_semaphore_new(0);
+
+ return a;
+}
+
+void pa_aupdate_free(pa_aupdate *a) {
+ pa_assert(a);
+
+ pa_mutex_free(a->write_lock);
+ pa_semaphore_free(a->semaphore);
+
+ pa_xfree(a);
+}
+
+unsigned pa_aupdate_read_begin(pa_aupdate *a) {
+ unsigned n;
+
+ pa_assert(a);
+
+ /* Increase the lock counter */
+ n = (unsigned) pa_atomic_inc(&a->read_lock);
+
+ /* When n is 0 we have about 2^31 threads running that all try to
+ * access the data at the same time, oh my! */
+ pa_assert(COUNTER(n)+1 > 0);
+
+ /* The uppermost bit tells us which data to look at */
+ return WHICH(n);
+}
+
+void pa_aupdate_read_end(pa_aupdate *a) {
+ unsigned n;
+
+ pa_assert(a);
+
+ /* Decrease the lock counter */
+ n = (unsigned) pa_atomic_dec(&a->read_lock);
+
+ /* Make sure the counter was valid */
+ pa_assert(COUNTER(n) > 0);
+
+ /* Post the semaphore */
+ pa_semaphore_post(a->semaphore);
+}
+
+unsigned pa_aupdate_write_begin(pa_aupdate *a) {
+ unsigned n;
+
+ pa_assert(a);
+
+ pa_mutex_lock(a->write_lock);
+
+ n = (unsigned) pa_atomic_load(&a->read_lock);
+
+ return !WHICH(n);
+}
+
+unsigned pa_aupdate_write_swap(pa_aupdate *a) {
+ unsigned n;
+
+ pa_assert(a);
+
+ for (;;) {
+ n = (unsigned) pa_atomic_load(&a->read_lock);
+
+ /* If the read counter is > 0 wait; if it is 0 try to swap the lists */
+ if (COUNTER(n) > 0)
+ pa_semaphore_wait(a->semaphore);
+ else if (pa_atomic_cmpxchg(&a->read_lock, (int) n, (int) (n ^ MSB)))
+ break;
+ }
+
+ return WHICH(n);
+}
+
+void pa_aupdate_write_end(pa_aupdate *a) {
+ pa_assert(a);
+
+ pa_mutex_unlock(a->write_lock);
+}
diff --git a/src/pulsecore/aupdate.h b/src/pulsecore/aupdate.h
new file mode 100644
index 00000000..072e382d
--- /dev/null
+++ b/src/pulsecore/aupdate.h
@@ -0,0 +1,98 @@
+#ifndef foopulsecoreaupdatehfoo
+#define foopulsecoreaupdatehfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 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
+ 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_aupdate pa_aupdate;
+
+pa_aupdate *pa_aupdate_new(void);
+void pa_aupdate_free(pa_aupdate *a);
+
+/* Will return 0, or 1, depending on which copy of the data the caller
+ * should look at */
+unsigned pa_aupdate_read_begin(pa_aupdate *a);
+void pa_aupdate_read_end(pa_aupdate *a);
+
+/* Will return 0, or 1, depending which copy of the data the caller
+ * should modify */
+unsigned pa_aupdate_write_begin(pa_aupdate *a);
+void pa_aupdate_write_end(pa_aupdate *a);
+
+/* Will return 0, or 1, depending which copy of the data the caller
+ * should modify. Each time called this will return the opposite of
+ * the previous pa_aupdate_write_begin()/pa_aupdate_write_swap()
+ * call. Should only be called between pa_aupdate_write_begin() and
+ * pa_aupdate_write_end() */
+unsigned pa_aupdate_write_swap(pa_aupdate *a);
+
+/*
+ * This infrastructure allows lock-free updates of arbitrary data
+ * structures in an rcu'ish way: two copies of the data structure
+ * should be exisiting. One side ('the reader') has read access to one
+ * of the two data structure at a time. It does not have to lock it,
+ * however it needs to signal that it is using it/stopped using
+ * it. The other side ('the writer') modifes the second data structure,
+ * and then atomically swaps the two data structures, followed by a
+ * modification of the other one.
+ *
+ * This is intended to be used for cases where the reader side needs
+ * to be fast while the writer side can be slow.
+ *
+ * The reader side is signal handler safe.
+ *
+ * The writer side lock is not recursive. The reader side is.
+ *
+ * There may be multiple readers and multiple writers at the same
+ * time.
+ *
+ * Usage is like this:
+ *
+ * static struct foo bar[2];
+ * static pa_aupdate *a;
+ *
+ * reader() {
+ * unsigned j;
+ *
+ * j = pa_update_read_begin(a);
+ *
+ * ... read the data structure bar[j] ...
+ *
+ * pa_update_read_end(a);
+ * }
+ *
+ * writer() {
+ * unsigned j;
+ *
+ * j = pa_update_write_begin(a);
+ *
+ * ... update the data structure bar[j] ...
+ *
+ * j = pa_update_write_swap(a);
+ *
+ * ... update the data structure bar[j], the same way as above ...
+ *
+ * pa_update_write_end(a)
+ * }
+ *
+ */
+
+#endif
diff --git a/src/pulsecore/memtrap.c b/src/pulsecore/memtrap.c
index e04e838e..bd0243e0 100644
--- a/src/pulsecore/memtrap.c
+++ b/src/pulsecore/memtrap.c
@@ -28,10 +28,10 @@
#include <pulse/xmalloc.h>
-#include <pulsecore/semaphore.h>
-#include <pulsecore/macro.h>
-#include <pulsecore/mutex.h>
#include <pulsecore/core-util.h>
+#include <pulsecore/aupdate.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/once.h>
#include "memtrap.h"
@@ -43,13 +43,13 @@ struct pa_memtrap {
};
static pa_memtrap *memtraps[2] = { NULL, NULL };
-static pa_atomic_t read_lock = PA_ATOMIC_INIT(0);
-static pa_static_semaphore semaphore = PA_STATIC_SEMAPHORE_INIT;
-static pa_static_mutex write_lock = PA_STATIC_MUTEX_INIT;
+static pa_aupdate *aupdate;
-#define MSB (1U << (sizeof(unsigned)*8U-1))
-#define WHICH(n) (!!((n) & MSB))
-#define COUNTER(n) ((n) & ~MSB)
+static void allocate_aupdate(void) {
+ PA_ONCE_BEGIN {
+ aupdate = pa_aupdate_new();
+ } PA_ONCE_END;
+}
pa_bool_t pa_memtrap_is_good(pa_memtrap *m) {
pa_assert(m);
@@ -62,19 +62,11 @@ static void sigsafe_error(const char *s) {
}
static void signal_handler(int sig, siginfo_t* si, void *data) {
- unsigned n, j;
+ unsigned j;
pa_memtrap *m;
void *r;
- /* Increase the lock counter */
- n = (unsigned) pa_atomic_inc(&read_lock);
-
- /* The uppermost bit tells us which list to look at */
- j = WHICH(n);
-
- /* When n is 0 we have about 2^31 threads running that
- * all got a sigbus at the same time, oh my! */
- pa_assert(COUNTER(n)+1 > 0);
+ j = pa_aupdate_read_begin(aupdate);
for (m = memtraps[j]; m; m = m->next[j])
if (si->si_addr >= m->start &&
@@ -94,33 +86,16 @@ static void signal_handler(int sig, siginfo_t* si, void *data) {
pa_assert(r == m->start);
- pa_atomic_dec(&read_lock);
-
- /* Post the semaphore */
- pa_semaphore_post(pa_static_semaphore_get(&semaphore, 0));
-
+ pa_aupdate_read_end(aupdate);
return;
fail:
+ pa_aupdate_read_end(aupdate);
+
sigsafe_error("Failed to handle SIGBUS.\n");
- pa_atomic_dec(&read_lock);
abort();
}
-static void memtrap_swap(unsigned n) {
-
- for (;;) {
-
- /* If the read counter is > 0 wait; if it is 0 try to swap the lists */
- if (COUNTER(n) > 0)
- pa_semaphore_wait(pa_static_semaphore_get(&semaphore, 0));
- else if (pa_atomic_cmpxchg(&read_lock, (int) n, (int) (n ^ MSB)))
- break;
-
- n = (unsigned) pa_atomic_load(&read_lock);
- }
-}
-
static void memtrap_link(pa_memtrap *m, unsigned j) {
pa_assert(m);
@@ -143,58 +118,47 @@ static void memtrap_unlink(pa_memtrap *m, unsigned j) {
pa_memtrap* pa_memtrap_add(const void *start, size_t size) {
pa_memtrap *m = NULL;
- pa_mutex *lock;
- unsigned n, j;
+ unsigned j;
pa_assert(start);
pa_assert(size > 0);
pa_assert(PA_PAGE_ALIGN_PTR(start) == start);
pa_assert(PA_PAGE_ALIGN(size) == size);
- lock = pa_static_mutex_get(&write_lock, FALSE, FALSE);
- pa_mutex_lock(lock);
-
- n = (unsigned) pa_atomic_load(&read_lock);
- j = WHICH(n);
-
m = pa_xnew(pa_memtrap, 1);
m->start = (void*) start;
m->size = size;
pa_atomic_store(&m->bad, 0);
- memtrap_link(m, !j);
- memtrap_swap(n);
- memtrap_link(m, j);
+ allocate_aupdate();
- pa_mutex_unlock(lock);
+ j = pa_aupdate_write_begin(aupdate);
+ memtrap_link(m, j);
+ j = pa_aupdate_write_swap(aupdate);
+ memtrap_link(m, j);
+ pa_aupdate_write_end(aupdate);
return m;
}
void pa_memtrap_remove(pa_memtrap *m) {
- unsigned n, j;
- pa_mutex *lock;
+ unsigned j;
pa_assert(m);
- lock = pa_static_mutex_get(&write_lock, FALSE, FALSE);
- pa_mutex_lock(lock);
+ allocate_aupdate();
- n = (unsigned) pa_atomic_load(&read_lock);
- j = WHICH(n);
-
- memtrap_unlink(m, !j);
- memtrap_swap(n);
+ j = pa_aupdate_write_begin(aupdate);
+ memtrap_unlink(m, j);
+ j = pa_aupdate_write_swap(aupdate);
memtrap_unlink(m, j);
+ pa_aupdate_write_end(aupdate);
pa_xfree(m);
-
- pa_mutex_unlock(lock);
}
pa_memtrap *pa_memtrap_update(pa_memtrap *m, const void *start, size_t size) {
- unsigned n, j;
- pa_mutex *lock;
+ unsigned j;
pa_assert(m);
@@ -203,32 +167,25 @@ pa_memtrap *pa_memtrap_update(pa_memtrap *m, const void *start, size_t size) {
pa_assert(PA_PAGE_ALIGN_PTR(start) == start);
pa_assert(PA_PAGE_ALIGN(size) == size);
- lock = pa_static_mutex_get(&write_lock, FALSE, FALSE);
- pa_mutex_lock(lock);
+ allocate_aupdate();
+
+ j = pa_aupdate_write_begin(aupdate);
if (m->start == start && m->size == size)
goto unlock;
- n = (unsigned) pa_atomic_load(&read_lock);
- j = WHICH(n);
-
- memtrap_unlink(m, !j);
- memtrap_swap(n);
memtrap_unlink(m, j);
+ j = pa_aupdate_write_swap(aupdate);
m->start = (void*) start;
m->size = size;
pa_atomic_store(&m->bad, 0);
- n = (unsigned) pa_atomic_load(&read_lock);
- j = WHICH(n);
-
- memtrap_link(m, !j);
- memtrap_swap(n);
+ j = pa_aupdate_write_swap(aupdate);
memtrap_link(m, j);
unlock:
- pa_mutex_unlock(lock);
+ pa_aupdate_write_end(aupdate);
return m;
}
@@ -236,10 +193,7 @@ unlock:
void pa_memtrap_install(void) {
struct sigaction sa;
- /* Before we install the signal handler, make sure the semaphore
- * is valid so that the initialization of the semaphore
- * doesn't have to happen from the signal handler */
- pa_static_semaphore_get(&semaphore, 0);
+ allocate_aupdate();
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = signal_handler;
diff --git a/src/pulsecore/memtrap.h b/src/pulsecore/memtrap.h
index f7da7083..fa38da58 100644
--- a/src/pulsecore/memtrap.h
+++ b/src/pulsecore/memtrap.h
@@ -34,7 +34,7 @@
* still 'good' i.e. no SIGBUS has happened yet for it.
*
* Intended usage is to handle memory mapped in which is controlled by
- * other processes that might execute ftruncate() or when mapping in
+ * other processes that might execute ftruncate() or when mapping inb
* hardware resources that might get invalidated when unplugged. */
typedef struct pa_memtrap pa_memtrap;