plugin: sort topological candidates by specified order.

We previously registered hooks up in who-replies-to-getmanifest-first
order, but then if any had dependencies it would scatter that order.

This allows users to manually set dependencies developers have
forgotten by specifying the plugins manually in their configuration or
cmdline.  This was an excellent consideration by @mschmook.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2020-11-03 09:50:52 +10:30
committed by neil saitug
parent d429e21db3
commit fb295ffb51
7 changed files with 66 additions and 66 deletions

View File

@@ -1,3 +1,4 @@
#include <ccan/asort/asort.h>
#include <ccan/io/io.h>
#include <ccan/list/list.h>
#include <common/configdir.h>
@@ -401,25 +402,9 @@ void plugin_hook_add_deps(struct plugin_hook *hook,
add_deps(&h->after, buffer, after);
}
/* From https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm
* (https://creativecommons.org/licenses/by-sa/3.0/):
* L ← Empty list that will contain the sorted elements
* S ← Set of all nodes with no incoming edge
*
* while S is not empty do
* remove a node n from S
* add n to L
* for each node m with an edge e from n to m do
* remove edge e from the graph
* if m has no other incoming edges then
* insert m into S
*
* if graph has edges then
* return error (graph has at least one cycle)
* else
* return L (a topologically sorted order)
*/
struct hook_node {
/* Is this copied into the ordered array yet? */
bool finished;
struct hook_instance *hook;
size_t num_incoming;
struct hook_node **outgoing;
@@ -434,16 +419,33 @@ static struct hook_node *find_hook(struct hook_node *graph, const char *name)
return NULL;
}
/* Sometimes naive is best. */
static struct hook_node *get_best_candidate(struct hook_node *graph)
{
struct hook_node *best = NULL;
for (size_t i = 0; i < tal_count(graph); i++) {
if (graph[i].finished)
continue;
if (graph[i].num_incoming != 0)
continue;
if (!best
|| best->hook->plugin->index > graph[i].hook->plugin->index)
best = &graph[i];
}
return best;
}
static struct plugin **plugin_hook_make_ordered(const tal_t *ctx,
struct plugin_hook *hook)
{
struct hook_node *graph;
struct hook_node **l, **s;
struct plugin **ret;
struct hook_node *graph, *n;
struct hook_instance **done;
/* Populate graph nodes */
graph = tal_arr(tmpctx, struct hook_node, tal_count(hook->hooks));
for (size_t i = 0; i < tal_count(graph); i++) {
graph[i].finished = false;
graph[i].hook = hook->hooks[i];
graph[i].num_incoming = 0;
graph[i].outgoing = tal_arr(graph, struct hook_node *, 0);
@@ -481,45 +483,26 @@ static struct plugin **plugin_hook_make_ordered(const tal_t *ctx,
}
}
/* Populate array of ready-to-go nodes. */
s = tal_arr(graph, struct hook_node *, 0);
for (size_t i = 0; i < tal_count(graph); i++) {
if (graph[i].num_incoming == 0)
tal_arr_expand(&s, &graph[i]);
done = tal_arr(tmpctx, struct hook_instance *, 0);
while ((n = get_best_candidate(graph)) != NULL) {
tal_arr_expand(&done, n->hook);
n->finished = true;
for (size_t i = 0; i < tal_count(n->outgoing); i++)
n->outgoing[i]->num_incoming--;
}
l = tal_arr(graph, struct hook_node *, 0);
while (tal_count(s)) {
struct hook_node *n = s[0];
tal_arr_expand(&l, n);
tal_arr_remove(&s, 0);
/* for each node m with an edge e from n to m do
* remove edge e from the graph
* if m has no other incoming edges then
* insert m into S
*/
for (size_t i = 0; i < tal_count(n->outgoing); i++) {
if (--n->outgoing[i]->num_incoming == 0)
tal_arr_expand(&s, n->outgoing[i]);
if (tal_count(done) != tal_count(hook->hooks)) {
struct plugin **ret = tal_arr(ctx, struct plugin *, 0);
for (size_t i = 0; i < tal_count(graph); i++) {
if (!graph[i].finished)
tal_arr_expand(&ret, graph[i].hook->plugin);
}
}
/* Check for any left over: these cannot be loaded. */
ret = tal_arr(ctx, struct plugin *, 0);
for (size_t i = 0; i < tal_count(graph); i++) {
if (graph[i].num_incoming)
tal_arr_expand(&ret, graph[i].hook->plugin);
}
if (tal_count(ret) != 0)
return ret;
}
/* Success! Write them back in order. */
assert(tal_count(l) == tal_count(hook->hooks));
for (size_t i = 0; i < tal_count(hook->hooks); i++)
hook->hooks[i] = l[i]->hook;
return tal_free(ret);
/* Success! Copy ordered hooks back. */
memcpy(hook->hooks, done, tal_bytelen(hook->hooks));
return NULL;
}
/* Plugins could fail due to multiple hooks, but only add once. */