From 0b87becb0ac81f75e52a8e7e6c71076e4f2e164e Mon Sep 17 00:00:00 2001 From: Chris Ayoub Date: Tue, 18 Aug 2026 17:49:35 +0000 Subject: [PATCH] core: clear dependency graph before freeing all units When the manager tears down every unit during reload or shutdown, unit_free() removes each dependency from both endpoints. This work is redundant because every dependency map is about to disappear, and it scales poorly with thousands of synthesized mount units. Clear all unit dependency maps in one manager-wide pass before invoking the existing unit_free() cleanup. With 10,000 bind mounts, this reduced the mean daemon-reload time from 63.558s to 14.708s. --- src/core/manager.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/core/manager.c b/src/core/manager.c index bf4d2333d9..0ccbaa61c5 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -1670,11 +1670,36 @@ static unsigned manager_dispatch_stop_notify_queue(Manager *m) { return n; } +static void manager_clear_unit_dependencies(Manager *m) { + Unit *u; + const char *name; + + assert(m); + + HASHMAP_FOREACH_KEY(u, name, m->units) { + + /* ignore aliases */ + if (u->id != name) + continue; + + for (Hashmap *dependencies; (dependencies = hashmap_steal_first(u->dependencies));) + hashmap_free(dependencies); + + u->dependencies = hashmap_free(u->dependencies); + u->dependency_generation++; + } +} + static void manager_clear_jobs_and_units(Manager *m) { Unit *u; assert(m); + /* All units are going away, hence discard the full dependency graph in one pass. Otherwise + * unit_free() removes every edge from both endpoints separately, which becomes very costly with + * large numbers of synthesized mount units. */ + manager_clear_unit_dependencies(m); + while ((u = hashmap_first(m->units))) unit_free(u);