Skip to content

Commit d6261ad

Browse files
committed
lightningd: keep a hash table for plugin notifications.
This means we don't have to iterate through all plugins, making our "do we even have to construct this notification" optimization much more efficient. Signed-off-by: Rusty Russell <[email protected]>
1 parent 7fe3a2a commit d6261ad

File tree

2 files changed

+96
-22
lines changed

2 files changed

+96
-22
lines changed

lightningd/plugin.c

+65-21
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ struct plugins *plugins_new(const tal_t *ctx, struct log_book *log_book,
7575
p->plugin_idx = 0;
7676
p->dev_builtin_plugins_unimportant = false;
7777
p->want_db_transaction = true;
78+
p->subscriptions = tal(p, struct plugin_subscription_htable);
79+
plugin_subscription_htable_init(p->subscriptions);
7880

7981
return p;
8082
}
@@ -86,13 +88,13 @@ static void plugin_check_subscriptions(struct plugins *plugins,
8688
struct plugin *plugin)
8789
{
8890
for (size_t i = 0; i < tal_count(plugin->subscriptions); i++) {
89-
const char *topic = plugin->subscriptions[i];
90-
if (!streq(topic, "*")
91-
&& !notifications_have_topic(plugins, topic))
91+
struct plugin_subscription *sub = &plugin->subscriptions[i];
92+
if (!streq(sub->topic, "*")
93+
&& !notifications_have_topic(plugins, sub->topic))
9294
log_unusual(
9395
plugin->log,
9496
"topic '%s' is not a known notification topic",
95-
topic);
97+
sub->topic);
9698
}
9799
}
98100

@@ -273,6 +275,12 @@ static void destroy_plugin(struct plugin *p)
273275
/* Now free all the requests */
274276
tal_free(reqs);
275277

278+
/* Remove any topics from the hash table */
279+
for (size_t i = 0; i < tal_count(p->subscriptions); i++) {
280+
plugin_subscription_htable_del(p->plugins->subscriptions,
281+
&p->subscriptions[i]);
282+
}
283+
276284
/* If this was last one manifests were waiting for, handle deps */
277285
if (p->plugin_state == AWAITING_GETMANIFEST_RESPONSE)
278286
check_plugins_manifests(p->plugins);
@@ -1474,13 +1482,13 @@ static const char *plugin_subscriptions_add(struct plugin *plugin,
14741482
plugin->subscriptions = NULL;
14751483
return NULL;
14761484
}
1477-
plugin->subscriptions = tal_arr(plugin, char *, 0);
1485+
plugin->subscriptions = tal_arr(plugin, struct plugin_subscription, 0);
14781486
if (subscriptions->type != JSMN_ARRAY) {
14791487
return tal_fmt(plugin, "\"result.subscriptions\" is not an array");
14801488
}
14811489

14821490
json_for_each_arr(i, s, subscriptions) {
1483-
char *topic;
1491+
struct plugin_subscription sub;
14841492
if (s->type != JSMN_STRING) {
14851493
return tal_fmt(plugin,
14861494
"result.subscriptions[%zu] is not a string: '%.*s'", i,
@@ -1492,9 +1500,17 @@ static const char *plugin_subscriptions_add(struct plugin *plugin,
14921500
* manifest, without checking that they exist, since
14931501
* later plugins may also emit notifications of custom
14941502
* types that we don't know about yet. */
1495-
topic = json_strdup(plugin, plugin->buffer, s);
1496-
tal_arr_expand(&plugin->subscriptions, topic);
1503+
sub.topic = json_strdup(plugin, plugin->buffer, s);
1504+
sub.owner = plugin;
1505+
tal_arr_expand(&plugin->subscriptions, sub);
14971506
}
1507+
1508+
/* Now they won't move with reallocation, we can add to htable */
1509+
for (i = 0; i < tal_count(plugin->subscriptions); i++) {
1510+
plugin_subscription_htable_add(plugin->plugins->subscriptions,
1511+
&plugin->subscriptions[i]);
1512+
}
1513+
14981514
return NULL;
14991515
}
15001516

@@ -2438,9 +2454,9 @@ static bool plugin_subscriptions_contains(struct plugin *plugin,
24382454
const char *method)
24392455
{
24402456
for (size_t i = 0; i < tal_count(plugin->subscriptions); i++) {
2441-
if (streq(method, plugin->subscriptions[i])
2457+
if (streq(method, plugin->subscriptions[i].topic)
24422458
|| is_asterix_notification(method,
2443-
plugin->subscriptions[i]))
2459+
plugin->subscriptions[i].topic))
24442460
return true;
24452461
}
24462462

@@ -2449,23 +2465,29 @@ static bool plugin_subscriptions_contains(struct plugin *plugin,
24492465

24502466
bool plugins_anyone_cares(struct plugins *plugins, const char *method)
24512467
{
2452-
struct plugin *p;
2468+
struct plugin_subscription_htable_iter it;
24532469

24542470
if (!plugins)
24552471
return false;
24562472

2457-
list_for_each(&plugins->plugins, p, list) {
2458-
if (plugin_subscriptions_contains(p, method))
2459-
return true;
2460-
}
2461-
return false;
2473+
if (plugin_subscription_htable_getfirst(plugins->subscriptions,
2474+
method, &it) != NULL)
2475+
return true;
2476+
2477+
/* Wildcards cover everything except "log" */
2478+
if (streq(method, "log"))
2479+
return false;
2480+
return plugin_subscription_htable_getfirst(plugins->subscriptions,
2481+
"*", &it) != NULL;
24622482
}
24632483

24642484
bool plugin_single_notify(struct plugin *p,
24652485
const struct jsonrpc_notification *n TAKES)
24662486
{
24672487
bool interested;
2468-
if (p->plugin_state == INIT_COMPLETE && plugin_subscriptions_contains(p, n->method)) {
2488+
2489+
if (p->plugin_state == INIT_COMPLETE
2490+
&& plugin_subscriptions_contains(p, n->method)) {
24692491
plugin_send(p, json_stream_dup(p, n->stream, p->log));
24702492
interested = true;
24712493
} else
@@ -2480,15 +2502,37 @@ bool plugin_single_notify(struct plugin *p,
24802502
void plugins_notify(struct plugins *plugins,
24812503
const struct jsonrpc_notification *n TAKES)
24822504
{
2483-
struct plugin *p;
2505+
struct plugin_subscription_htable_iter it;
24842506

24852507
if (taken(n))
24862508
tal_steal(tmpctx, n);
24872509

24882510
/* If we're shutting down, ld->plugins will be NULL */
2489-
if (plugins) {
2490-
list_for_each(&plugins->plugins, p, list) {
2491-
plugin_single_notify(p, n);
2511+
if (!plugins)
2512+
return;
2513+
2514+
for (struct plugin_subscription *sub
2515+
= plugin_subscription_htable_getfirst(plugins->subscriptions,
2516+
n->method, &it);
2517+
sub != NULL;
2518+
sub = plugin_subscription_htable_getnext(plugins->subscriptions,
2519+
n->method, &it)) {
2520+
if (sub->owner->plugin_state != INIT_COMPLETE)
2521+
continue;
2522+
plugin_send(sub->owner, json_stream_dup(sub->owner, n->stream, sub->owner->log));
2523+
}
2524+
2525+
/* "log" doesn't go to wildcards */
2526+
if (!streq(n->method, "log")) {
2527+
for (struct plugin_subscription *sub
2528+
= plugin_subscription_htable_getfirst(plugins->subscriptions,
2529+
"*", &it);
2530+
sub != NULL;
2531+
sub = plugin_subscription_htable_getnext(plugins->subscriptions,
2532+
"*", &it)) {
2533+
if (sub->owner->plugin_state != INIT_COMPLETE)
2534+
continue;
2535+
plugin_send(sub->owner, json_stream_dup(sub->owner, n->stream, sub->owner->log));
24922536
}
24932537
}
24942538
}

lightningd/plugin.h

+31-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,33 @@ enum plugin_state {
1919
INIT_COMPLETE
2020
};
2121

22+
struct plugin_subscription {
23+
struct plugin *owner;
24+
const char *topic;
25+
};
26+
27+
static inline size_t hash_str(const char *str)
28+
{
29+
return siphash24(siphash_seed(), str, strlen(str));
30+
}
31+
32+
static inline const char *plugin_subscription_key(const struct plugin_subscription *ps)
33+
{
34+
return ps->topic;
35+
}
36+
37+
static inline bool plugin_subscription_eq_topic(const struct plugin_subscription *ps,
38+
const char *topic)
39+
{
40+
return streq(ps->topic, topic);
41+
}
42+
43+
HTABLE_DEFINE_DUPS_TYPE(struct plugin_subscription,
44+
plugin_subscription_key,
45+
hash_str,
46+
plugin_subscription_eq_topic,
47+
plugin_subscription_htable);
48+
2249
/**
2350
* A plugin, exposed as a stub so we can pass it as an argument.
2451
*/
@@ -71,7 +98,7 @@ struct plugin {
7198
const struct oneshot *timeout_timer;
7299

73100
/* An array of subscribed topics */
74-
char **subscriptions;
101+
struct plugin_subscription *subscriptions;
75102

76103
/* Our pending requests by their request ID */
77104
STRMAP(struct jsonrpc_request *) pending_requests;
@@ -122,6 +149,9 @@ struct plugins {
122149
/* Index to show what order they were added in */
123150
u64 plugin_idx;
124151

152+
/* Table of who subscribed to what hooks/notifications */
153+
struct plugin_subscription_htable *subscriptions;
154+
125155
/* Whether builtin plugins should be overridden as unimportant. */
126156
bool dev_builtin_plugins_unimportant;
127157
};

0 commit comments

Comments
 (0)