2 * Copyright (c) 2012, Intel Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Intel Corporation nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include <murphy/common/mm.h>
33 #include <murphy/common/debug.h>
34 #include <murphy/common/log.h>
38 #include "resolver-types.h"
41 #include "target-sorter.h"
45 * dependency graph used to determine target update orders
49 mrp_resolver_t *r; /* resolver context */
50 int *edges; /* edges between nodes */
51 int nnode; /* number of graph nodes */
55 static graph_t *build_graph(mrp_resolver_t *r);
56 static int sort_graph(graph_t *g, int target_idx);
57 static void free_graph(graph_t *g);
58 static void dump_graph(graph_t *g, FILE *fp);
61 int sort_targets(mrp_resolver_t *r)
69 dump_graph(g, stdout);
73 for (i = 0; i < r->ntarget; i++) {
74 target_t *t = r->targets + i;
76 mrp_free(t->update_targets);
77 mrp_free(t->update_facts);
78 mrp_free(t->fact_stamps);
80 t->update_targets = NULL;
81 t->update_facts = NULL;
82 t->fact_stamps = NULL;
86 for (i = 0; i < r->ntarget; i++) {
87 if (sort_graph(g, i) < 0) {
88 mrp_log_error("Failed to determine update order for "
89 "resolver target '%s'.", r->targets[i].name);
92 mrp_log_error("Cyclic dependency detected.");
108 static inline int fact_id(graph_t *g, char *fact)
112 for (i = 0; i < g->r->nfact; i++)
113 if (!strcmp(fact, g->r->facts[i].name))
120 static inline int target_id(graph_t *g, char *target)
124 for (i = 0; i < g->r->ntarget; i++)
125 if (!strcmp(target, g->r->targets[i].name))
126 return g->r->nfact + i;
132 static inline char *node_name(graph_t *g, int id)
134 if (id < g->r->nfact)
135 return g->r->facts[id].name;
137 return g->r->targets[id - g->r->nfact].name;
141 static inline int node_id(graph_t *g, char *name)
144 return fact_id(g, name);
146 return target_id(g, name);
150 static inline int *edge_markp(graph_t *g, int n1, int n2)
152 static int invalid = 0;
154 if (n1 >= 0 && n2 >= 0)
155 return g->edges + (n1 * g->nnode) + n2;
161 static inline void mark_node(graph_t *g, int node)
163 *edge_markp(g, node, node) = 1;
167 static inline void unmark_node(graph_t *g, int node)
169 *edge_markp(g, node, node) = 0;
173 static inline int node_present(graph_t *g, int node)
175 return *edge_markp(g, node, node) == 1;
179 static graph_t *build_graph(mrp_resolver_t *r)
185 g = mrp_allocz(sizeof(*g));
189 g->nnode = r->nfact + r->ntarget;
190 g->edges = mrp_allocz(g->nnode * g->nnode * sizeof(*g->edges));
192 if (g->edges == NULL)
195 for (i = 0; i < r->ntarget; i++) {
199 for (j = 0; j < t->ndepend; j++) {
200 mrp_debug("adding edge: %s <- %s", t->depends[j], t->name);
201 did = node_id(g, t->depends[j]);
206 *edge_markp(g, did, tid) = 1;
222 static int mark_present_nodes(graph_t *g, int target_idx)
227 tid = g->r->nfact + target_idx;
228 t = g->r->targets + target_idx;
230 if (node_present(g, tid))
235 for (i = 0; i < t->ndepend; i++) {
236 did = node_id(g, t->depends[i]);
241 if (*t->depends[i] == '$')
244 if (!mark_present_nodes(g, did - g->r->nfact))
254 * queues we use for topological sorting
258 int size; /* max queue capacity */
259 int *items; /* queue item buffer */
260 int head; /* push index */
261 int tail; /* pop index */
264 #define EMPTY_QUE {.items = NULL } /* initializer for empty queue */
267 static int que_init(que_t *q, int size)
271 q->items = mrp_alloc(size * sizeof(*q->items));
273 if (q->items != NULL) {
285 static void que_cleanup(que_t *q)
292 static void que_push(que_t *q, int item)
294 /* we know the max size, so we don't check for overflow here */
295 q->items[q->tail++] = item;
300 static int que_pop(que_t *q, int *itemp)
302 if (q->head != q->tail) {
303 *itemp = q->items[q->head++];
315 static int sort_graph(graph_t *g, int target_idx)
320 * We perform a topological sort of the dependency graph here
321 * for our target with the given idx. We include only nodes
322 * for facts and targets which are relevant for our target.
323 * These are the ones which our target directly or indirectly
324 * depends on. We use the otherwise unused diagonal (no target
325 * can depend on itself) of our edge matrix to mark which nodes
326 * are present in the graph. Then we use the following algorithm
327 * to sort the subgraph of relevant nodes:
329 * initialize que L to be empty
330 * initialize que Q with all nodes without incoming edges
331 * while Q is not empty
332 * pop a node <n> from Q
334 * remove all edges that start from <n>
335 * for all nodes <m> where we removed an incoming node
336 * if <m> has no more incoming edges
339 * if there are any remaining edges
340 * return an error about cyclic dependency
342 * L is the sorted subgraph (our target is the last item in L)
344 * The resulted sort order of our target is then used as the
345 * dependency check/update order when the resolver is asked to
346 * update that target.
350 int edges[g->nnode * g->nnode];
351 que_t L = EMPTY_QUE, Q = EMPTY_QUE;
352 int i, j, m, id, node, nedge, nfact, ntarget;
354 target = g->r->targets + target_idx;
356 /* save full graph */
357 memcpy(edges, g->edges, sizeof(edges));
359 if (!que_init(&L, g->nnode + 1) || !que_init(&Q, g->nnode))
362 /* find and mark relevant nodes in the graph */
363 mark_present_nodes(g, target_idx);
365 mrp_debug("-- target %s --", target->name);
366 /*dump_graph(g, stdout);*/
368 /* push all relevant facts, they do not depend on anything */
369 for (i = 0; i < g->r->nfact; i++) {
371 if (node_present(g, id)) {
377 /* push all relevant targets that have no dependencies */
378 for (i = 0; i < g->r->ntarget; i++) {
379 id = g->r->nfact + i;
380 if (g->r->targets[i].depends == NULL && node_present(g, id)) {
386 /* try sorting the marked subgraph */
387 while (que_pop(&Q, &node)) {
390 mrp_debug("popped node %s", node_name(g, node));
392 for (m = 0; m < g->nnode; m++) {
393 if (m == node || !node_present(g, m))
395 *edge_markp(g, node, m) = 0;
397 for (j = 0; j < g->nnode; j++) {
400 if (node_present(g, j) && *edge_markp(g, j, m))
404 mrp_debug("node %s empty, pushing it", node_name(g, m));
409 mrp_debug("node %s not empty yet", node_name(g, m));
413 /* check if the subgraph has any remaining edges */
415 for (node = 0; node < g->nnode; node++) {
416 if (!node_present(g, node))
418 for (m = 0; m < g->nnode; m++) {
419 if (m == node || !node_present(g, m))
421 if (*edge_markp(g, node, m) == 1) {
428 mrp_debug("----- %s: graph sorted successfully -----", target->name);
430 for (i = 0; i < L.tail; i++)
431 mrp_debug(" %s", node_name(g, L.items[i]));
434 /* save the result in the given target */
439 for (i = 0; i < L.tail; i++) {
440 if (L.items[i] < g->r->nfact)
447 target->update_facts = mrp_alloc_array(int, nfact + 1);
448 target->fact_stamps = mrp_allocz_array(uint32_t, nfact);
450 if (target->update_facts != NULL && target->fact_stamps != NULL) {
451 for (i = 0; i < nfact; i++)
452 target->update_facts[i] = L.items[i];
453 target->update_facts[i] = -1;
460 target->update_targets = mrp_alloc_array(int, ntarget + 1);
461 if (target->update_targets != NULL) {
462 for (i = 0; i < ntarget; i++)
463 target->update_targets[i] = L.items[nfact+i] - g->r->nfact;
464 target->update_targets[i] = -1;
471 target->directs = mrp_allocz_array(int, target->ndepend);
473 if (target->ndepend == 0 || target->directs != NULL) {
477 for (i = 0; i < target->ndepend; i++) {
478 if (*target->depends[i] != '$')
481 f = lookup_fact(g->r, target->depends[i]);
484 target->directs[target->ndirect++] = f - g->r->facts;
486 target->directs[target->ndirect++] = -1;
489 for (i = 0; i < target->ndepend; i++) {
490 if (*target->depends[i] == '$')
493 t = lookup_target(g->r, target->depends[i]);
496 target->directs[target->ndirect++] = g->r->nfact +
499 target->directs[target->ndirect++] = -1;
509 /* restore the original full graph */
510 memcpy(g->edges, edges, sizeof(edges));
518 memcpy(g->edges, edges, sizeof(edges));
524 static void free_graph(graph_t *g)
533 static void dump_graph(graph_t *g, FILE *fp)
537 fprintf(fp, "Graph edges:\n");
539 fprintf(fp, " %20.20s ", "");
540 for (i = 0; i < g->nnode; i++)
541 fprintf(fp, " %d", i % 10);
544 for (i = 0; i < g->nnode; i++) {
545 fprintf(fp, " %20.20s: ", node_name(g, i));
546 for (j = 0; j < g->nnode; j++)
547 fprintf(fp, "%d ", *edge_markp(g, i, j));