[pulseaudio-discuss] [PATCH 08/15] core, edge: Add a basic pa_edge implementation

Tanu Kaskinen tanu.kaskinen at linux.intel.com
Thu Feb 13 19:35:53 CET 2014


An edge represents a connection between two nodes. The edge objects
will hide the details of setting up and tearing down connections, so
that routing logic can just say "create an edge from this node to that
node", instead of handling each node type separately.

Nobody calls pa_core_create_edge() and pa_core_delete_edge() yet, so
this code doesn't have any effect on anything at this point. That's
good, because the edge implementation doesn't actually do anything
useful yet...
---
 src/Makefile.am      |   2 +-
 src/pulsecore/core.c |  74 ++++++++++++++++++++++++++++
 src/pulsecore/core.h |  12 ++++-
 src/pulsecore/edge.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/pulsecore/edge.h |  23 +++++++++
 5 files changed, 244 insertions(+), 3 deletions(-)
 create mode 100644 src/pulsecore/edge.c

diff --git a/src/Makefile.am b/src/Makefile.am
index 7498a65..2068704 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -875,7 +875,7 @@ libpulsecore_ at PA_MAJORMINOR@_la_SOURCES = \
 		pulsecore/core-scache.c pulsecore/core-scache.h \
 		pulsecore/core-subscribe.c pulsecore/core-subscribe.h \
 		pulsecore/core.c pulsecore/core.h \
-		pulsecore/edge.h \
+		pulsecore/edge.c pulsecore/edge.h \
 		pulsecore/fdsem.c pulsecore/fdsem.h \
 		pulsecore/hook-list.c pulsecore/hook-list.h \
 		pulsecore/ltdl-helper.c pulsecore/ltdl-helper.h \
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index f3bf588..f88fead 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -32,6 +32,7 @@
 #include <pulse/timeval.h>
 #include <pulse/xmalloc.h>
 
+#include <pulsecore/edge.h>
 #include <pulsecore/module.h>
 #include <pulsecore/core-rtclock.h>
 #include <pulsecore/core-util.h>
@@ -100,9 +101,11 @@ pa_core* pa_core_new(pa_mainloop_api *m, bool shared, size_t shm_size) {
     c->modules = pa_idxset_new(NULL, NULL);
     c->scache = pa_idxset_new(NULL, NULL);
     c->nodes = pa_idxset_new(NULL, NULL);
+    c->edges = pa_idxset_new(NULL, NULL);
 
     c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
     c->shared = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    c->edges_by_key = pa_hashmap_new(pa_edge_key_hash_func, pa_edge_key_compare_func);
 
     c->default_source = NULL;
     c->default_sink = NULL;
@@ -170,6 +173,12 @@ static void core_free(pa_object *o) {
     /* Note: All modules and samples in the cache should be unloaded before
      * we get here */
 
+    pa_assert(pa_hashmap_isempty(c->edges_by_key));
+    pa_hashmap_free(c->edges_by_key);
+
+    pa_assert(pa_idxset_isempty(c->edges));
+    pa_idxset_free(c->edges, NULL);
+
     pa_assert(pa_idxset_isempty(c->nodes));
     pa_idxset_free(c->nodes, NULL);
 
@@ -220,6 +229,71 @@ static void core_free(pa_object *o) {
     pa_xfree(c);
 }
 
+pa_edge *pa_core_get_edge(pa_core *c, pa_node *input, pa_node *output) {
+    uint64_t key;
+
+    pa_assert(c);
+    pa_assert(input);
+    pa_assert(output);
+
+    key = pa_edge_key(input->index, output->index);
+
+    return pa_hashmap_get(c->edges_by_key, &key);
+}
+
+int pa_core_create_edge(pa_core *c, pa_node *input, pa_node *output, pa_edge **edge) {
+    pa_edge *edge_local = NULL;
+    int r;
+
+    pa_assert(c);
+    pa_assert(input);
+    pa_assert(output);
+
+    edge_local = pa_core_get_edge(c, input, output);
+    if (edge_local) {
+        pa_log_info("Edge %s already exists.", edge_local->string);
+        return -PA_ERR_EXIST;
+    }
+
+    if (pa_idxset_size(c->edges) == PA_MAX_EDGES) {
+        pa_log_info("Can't create edge %s -> %s, maximum number of edges reached.", input->name, output->name);
+        r = -PA_ERR_TOOLARGE;
+        goto fail;
+    }
+
+    edge_local = pa_edge_new(input, output, &r);
+    if (r < 0)
+        goto fail;
+
+    pa_assert_se(pa_idxset_put(c->edges, edge_local, &edge_local->index) >= 0);
+    pa_assert_se(pa_hashmap_put(c->edges_by_key, &edge_local->key, edge_local) >= 0);
+
+    if (edge)
+        *edge = edge_local;
+
+    return 0;
+
+fail:
+    if (edge_local)
+        pa_edge_free(edge_local);
+
+    return r;
+}
+
+void pa_core_delete_edge(pa_core *c, pa_edge *edge) {
+    pa_assert(c);
+    pa_assert(edge);
+
+    /* pa_edge_free() may cause a recursive call to pa_core_delete_edge(), so
+     * we need to be careful here. Let's call pa_edge_free() only if the edge
+     * hasn't yet been removed from the idxset, that way we can ensure that
+     * pa_edge_free() will be called only once. */
+    if (pa_idxset_remove_by_index(c->edges, edge->index)) {
+        pa_hashmap_remove(c->edges_by_key, &edge->key);
+        pa_edge_free(edge);
+    }
+}
+
 static void exit_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
     pa_core *c = userdata;
     pa_assert(c->exit_event == e);
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index bce6a6d..9127305 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -28,6 +28,10 @@
 
 typedef struct pa_core pa_core;
 
+/* Forward declaration to avoid circular #includes. (Also avoids recompiling
+ * the world every time edge.h changes.) */
+typedef struct pa_edge pa_edge;
+
 /* This is a bitmask that encodes the cause why a sink/source is
  * suspended. */
 typedef enum pa_suspend_cause {
@@ -249,10 +253,10 @@ struct pa_core {
     pa_mainloop_api *mainloop;
 
     /* idxset of all kinds of entities */
-    pa_idxset *clients, *cards, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *nodes;
+    pa_idxset *clients, *cards, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *nodes, *edges;
 
     /* Some hashmaps for all sorts of entities */
-    pa_hashmap *namereg, *shared;
+    pa_hashmap *namereg, *shared, *edges_by_key;
 
     /* The default sink/source */
     pa_source *default_source;
@@ -309,6 +313,10 @@ enum {
 
 pa_core* pa_core_new(pa_mainloop_api *m, bool shared, size_t shm_size);
 
+pa_edge *pa_core_get_edge(pa_core *c, pa_node *input, pa_node *output);
+int pa_core_create_edge(pa_core *c, pa_node *input, pa_node *output, pa_edge **edge);
+void pa_core_delete_edge(pa_core *c, pa_edge *edge);
+
 /* Check whether no one is connected to this core */
 void pa_core_check_idle(pa_core *c);
 
diff --git a/src/pulsecore/edge.c b/src/pulsecore/edge.c
new file mode 100644
index 0000000..da40a53
--- /dev/null
+++ b/src/pulsecore/edge.c
@@ -0,0 +1,136 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright (c) 2014 Intel Corporation
+
+  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 "edge.h"
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/device-port.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/node.h>
+
+#include <pulse/xmalloc.h>
+
+uint64_t pa_edge_key(uint32_t input_index, uint32_t output_index) {
+    return ((uint64_t) input_index) << 32 | (uint64_t) output_index;
+}
+
+unsigned pa_edge_key_hash_func(const void *p) {
+    static const uint32_t input_mask[16] = {
+        /* 00000000, 00000010, 00001000, 00001010, 00100000, 00100010, 00101000, 00101010, */
+           0x00,     0x02,     0x08,     0x0A,     0x20,     0x22,     0x28,     0x2A,
+        /* 10000000, 10000010, 10001000, 10001010, 10100000, 10100010, 10101000, 10101010 */
+           0x80,     0x82,     0x88,     0x8A,     0xA0,     0xA2,     0xA8,     0xAA
+    };
+    static const uint32_t output_mask[16] = {
+        /* 00000000, 00000001, 00000100, 00000101, 00010000, 00010001, 00010100, 00010101, */
+           0x00,     0x01,     0x04,     0x05,     0x10,     0x11,     0x14,     0x15,
+        /* 01000000, 01000001, 01000100, 01000101, 01010000, 01010001, 01010100, 01010101 */
+           0x40,     0x41,     0x44,     0x45,     0x50,     0x51,     0x54,     0x55
+    };
+    uint64_t key = *((uint64_t *) p);
+    int i;
+    uint32_t hash;
+    uint32_t input_index = ((key >> 32) & 0xffff);
+    uint32_t output_index = key & 0xffff;
+
+    /* What does the loop below do?
+     *
+     * The loop runs four times. The first iteration calculates the first (most
+     * significant) byte of the hash, the second iteration calculates the
+     * second byte, and so on. The result is a 32-bit hash value.
+     *
+     * The input and output node indexes are used as input for the hash. The
+     * upper 16 bits of the 32-bit indexes are ignored.
+     *
+     * The first byte of the hash consists of the lowest 4 bits of the input
+     * index interleaved with the lowest 4 bits of the output index. The second
+     * byte consists of the next 4 bits of the indexes and so on. So, the hash
+     * ends up containing the lower halves of both indexes interleaved, with
+     * the twist that the least significant bits of the indexes become the most
+     * significant bits of the hash and vice versa. */
+
+    for (i = 0, hash = 0; i < 4; i++, input_index >>= 4, output_index >>= 4)
+        hash = (hash << 8) | (input_mask[input_index & 15] | output_mask[output_index & 15]);
+
+    return hash;
+}
+
+int pa_edge_key_compare_func(const void *a, const void *b) {
+    uint64_t key1 = *((uint64_t *) a);
+    uint64_t key2 = *((uint64_t *) b);
+
+    if (key1 > key2)
+        return 1;
+
+    if (key2 < key1)
+        return -1;
+
+    return 0;
+}
+
+pa_edge *pa_edge_new(pa_node *input, pa_node *output, int *ret) {
+    pa_edge *edge = NULL;
+    int r;
+
+    pa_assert(input);
+    pa_assert(output);
+
+    if (input->direction != PA_DIRECTION_INPUT) {
+        pa_log_info("Invalid direction: tried to use %s as input.", input->name);
+        r = -PA_ERR_INVALID;
+        goto fail;
+    }
+
+    if (output->direction != PA_DIRECTION_OUTPUT) {
+        pa_log_info("Invalid direction: tried to use %s as output.", output->name);
+        r = -PA_ERR_INVALID;
+        goto fail;
+    }
+
+    edge = pa_xnew0(pa_edge, 1);
+    edge->index = PA_INVALID_INDEX;
+    edge->key = pa_edge_key(input->index, output->index);
+    edge->input = input;
+    edge->output = output;
+    edge->string = pa_sprintf_malloc("%s -> %s", input->name, output->name);
+
+    return edge;
+
+fail:
+    if (edge)
+        pa_edge_free(edge);
+
+    if (ret)
+        *ret = r;
+
+    return NULL;
+}
+
+void pa_edge_free(pa_edge *edge) {
+    pa_assert(edge);
+
+    pa_xfree(edge->string);
+    pa_xfree(edge);
+}
diff --git a/src/pulsecore/edge.h b/src/pulsecore/edge.h
index e7f73ea..880df90 100644
--- a/src/pulsecore/edge.h
+++ b/src/pulsecore/edge.h
@@ -22,6 +22,29 @@
   USA.
 ***/
 
+#include <inttypes.h>
+#include <stdbool.h>
+
+typedef struct pa_edge pa_edge;
+
+/* Forward declaration to avoid circular #includes. */
+typedef struct pa_node pa_node;
+
+struct pa_edge {
+    uint32_t index;
+    uint64_t key;
+    pa_node *input;
+    pa_node *output;
+    char *string;
+};
+
 #define PA_MAX_EDGES 1024
 
+uint64_t pa_edge_key(uint32_t input_index, uint32_t output_index);
+unsigned pa_edge_key_hash_func(const void *p);
+int pa_edge_key_compare_func(const void *a, const void *b);
+
+pa_edge *pa_edge_new(pa_node *input, pa_node *output, int *ret);
+void pa_edge_free(pa_edge *edge);
+
 #endif
-- 
1.8.3.1



More information about the pulseaudio-discuss mailing list