aboutsummaryrefslogtreecommitdiffstats
path: root/src/util/dag.c
diff options
context:
space:
mode:
authorEric Anholt <[email protected]>2019-02-27 11:12:59 -0800
committerEric Anholt <[email protected]>2019-03-11 13:13:52 -0700
commit0533d2d95c0df559e78de3bfeb8e8ae5b7fc8140 (patch)
tree3e6ef85a99e81b42e529242b449f1afb10a69f4a /src/util/dag.c
parent5f0a922c277639abfd03c27c160b327a7889162b (diff)
util: Add a DAG datastructure.
I keep writing this for various schedulers. Acked-by: Timothy Arceri <[email protected]>
Diffstat (limited to 'src/util/dag.c')
-rw-r--r--src/util/dag.c154
1 files changed, 154 insertions, 0 deletions
diff --git a/src/util/dag.c b/src/util/dag.c
new file mode 100644
index 00000000000..8f7cdd6348e
--- /dev/null
+++ b/src/util/dag.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright © 2019 Broadcom
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "util/set.h"
+#include "util/dag.h"
+
+/**
+ * Adds a directed edge from the parent node to the child.
+ *
+ * Both nodes should have been initialized with dag_init_node(). The edge
+ * list may contain multiple edges to the same child with different data.
+ */
+void
+dag_add_edge(struct dag_node *parent, struct dag_node *child, void *data)
+{
+ util_dynarray_foreach(&parent->edges, struct dag_edge, edge) {
+ if (edge->child == child && edge->data == data)
+ return;
+ }
+ /* Remove the child as a DAG head. */
+ list_delinit(&child->link);
+
+ struct dag_edge edge = {
+ .child = child,
+ .data = data,
+ };
+
+ util_dynarray_append(&parent->edges, struct dag_edge, edge);
+ child->parent_count++;
+}
+
+/* Removes a single edge from the graph, promoting the child to a DAG head.
+ *
+ * Note that calling this other than through dag_prune_head() means that you
+ * need to be careful when iterating the edges of remaining nodes for NULL
+ * children.
+ */
+void
+dag_remove_edge(struct dag *dag, struct dag_edge *edge)
+{
+ if (!edge->child)
+ return;
+
+ struct dag_node *child = edge->child;
+ child->parent_count--;
+ if (child->parent_count == 0)
+ list_addtail(&child->link, &dag->heads);
+
+ edge->child = NULL;
+ edge->data = NULL;
+}
+
+/**
+ * Removes a DAG head from the graph, and moves any new dag heads into the
+ * heads list.
+ */
+void
+dag_prune_head(struct dag *dag, struct dag_node *node)
+{
+ assert(!node->parent_count);
+
+ list_delinit(&node->link);
+
+ util_dynarray_foreach(&node->edges, struct dag_edge, edge) {
+ dag_remove_edge(dag, edge);
+ }
+}
+
+/**
+ * Initializes DAG node (probably embedded in some other datastructure in the
+ * user).
+ */
+void
+dag_init_node(struct dag *dag, struct dag_node *node)
+{
+ util_dynarray_init(&node->edges, dag);
+ list_addtail(&node->link, &dag->heads);
+}
+
+struct dag_traverse_bottom_up_state {
+ struct set *seen;
+ void *data;
+};
+
+static void
+dag_traverse_bottom_up_node(struct dag_node *node,
+ void (*cb)(struct dag_node *node,
+ void *data),
+ struct dag_traverse_bottom_up_state *state)
+{
+ if (_mesa_set_search(state->seen, node))
+ return;
+
+ util_dynarray_foreach(&node->edges, struct dag_edge, edge) {
+ dag_traverse_bottom_up_node(edge->child, cb, state);
+ }
+
+ cb(node, state->data);
+ _mesa_set_add(state->seen, node);
+}
+
+/**
+ * Walks the DAG from leaves to the root, ensuring that each node is only seen
+ * once its children have been, and each node is only traversed once.
+ */
+void
+dag_traverse_bottom_up(struct dag *dag, void (*cb)(struct dag_node *node,
+ void *data), void *data)
+{
+ struct dag_traverse_bottom_up_state state = {
+ .seen = _mesa_pointer_set_create(NULL),
+ .data = data,
+ };
+
+ list_for_each_entry(struct dag_node, node, &dag->heads, link) {
+ dag_traverse_bottom_up_node(node, cb, &state);
+ }
+
+ ralloc_free(state.seen);
+}
+
+/**
+ * Creates an empty DAG datastructure.
+ */
+struct dag *
+dag_create(void *mem_ctx)
+{
+ struct dag *dag = rzalloc(mem_ctx, struct dag);
+
+ list_inithead(&dag->heads);
+
+ return dag;
+}
+