Zbigniew Jędrzejewski-Szmek 399a2a
From 3ef091125ceb159374d2d0d4dbad84d90b252852 Mon Sep 17 00:00:00 2001
Zbigniew Jędrzejewski-Szmek 399a2a
From: Ivan Shapovalov <intelfx100@gmail.com>
Zbigniew Jędrzejewski-Szmek 399a2a
Date: Sat, 7 Mar 2015 08:44:52 -0500
Zbigniew Jędrzejewski-Szmek 399a2a
Subject: [PATCH] core: do not spawn jobs or touch other units during
Zbigniew Jędrzejewski-Szmek 399a2a
 coldplugging
Zbigniew Jędrzejewski-Szmek 399a2a
Zbigniew Jędrzejewski-Szmek 399a2a
Because the order of coldplugging is not defined, we can reference a
Zbigniew Jędrzejewski-Szmek 399a2a
not-yet-coldplugged unit and read its state while it has not yet been
Zbigniew Jędrzejewski-Szmek 399a2a
set to a meaningful value.
Zbigniew Jędrzejewski-Szmek 399a2a
Zbigniew Jędrzejewski-Szmek 399a2a
This way, already active units may get started again.
Zbigniew Jędrzejewski-Szmek 399a2a
Zbigniew Jędrzejewski-Szmek 399a2a
We fix this by deferring such actions until all units have been at
Zbigniew Jędrzejewski-Szmek 399a2a
least somehow coldplugged.
Zbigniew Jędrzejewski-Szmek 399a2a
Zbigniew Jędrzejewski-Szmek 399a2a
Fixes https://bugs.freedesktop.org/show_bug.cgi?id=88401
Zbigniew Jędrzejewski-Szmek 399a2a
Zbigniew Jędrzejewski-Szmek 399a2a
(cherry picked from commit 6e392c9c45643d106673c6643ac8bf4e65da13c1)
Zbigniew Jędrzejewski-Szmek 399a2a
---
Zbigniew Jędrzejewski-Szmek 399a2a
 src/core/automount.c |  2 +-
Zbigniew Jędrzejewski-Szmek 399a2a
 src/core/busname.c   |  2 +-
Zbigniew Jędrzejewski-Szmek 399a2a
 src/core/device.c    |  2 +-
Zbigniew Jędrzejewski-Szmek 399a2a
 src/core/manager.c   | 35 +++++++++++++++++++++++++++++++++--
Zbigniew Jędrzejewski-Szmek 399a2a
 src/core/mount.c     |  2 +-
Zbigniew Jędrzejewski-Szmek 399a2a
 src/core/path.c      | 14 ++++++++++----
Zbigniew Jędrzejewski-Szmek 399a2a
 src/core/scope.c     |  2 +-
Zbigniew Jędrzejewski-Szmek 399a2a
 src/core/service.c   |  2 +-
Zbigniew Jędrzejewski-Szmek 399a2a
 src/core/slice.c     |  2 +-
Zbigniew Jędrzejewski-Szmek 399a2a
 src/core/snapshot.c  |  2 +-
Zbigniew Jędrzejewski-Szmek 399a2a
 src/core/socket.c    |  2 +-
Zbigniew Jędrzejewski-Szmek 399a2a
 src/core/swap.c      |  2 +-
Zbigniew Jędrzejewski-Szmek 399a2a
 src/core/target.c    |  2 +-
Zbigniew Jędrzejewski-Szmek 399a2a
 src/core/timer.c     | 14 ++++++++++----
Zbigniew Jędrzejewski-Szmek 399a2a
 src/core/unit.c      | 25 ++++++++++++++++---------
Zbigniew Jędrzejewski-Szmek 399a2a
 src/core/unit.h      | 12 +++++++++---
Zbigniew Jędrzejewski-Szmek 399a2a
 16 files changed, 89 insertions(+), 33 deletions(-)
Zbigniew Jędrzejewski-Szmek 399a2a
Zbigniew Jędrzejewski-Szmek 399a2a
diff --git a/src/core/automount.c b/src/core/automount.c
Zbigniew Jędrzejewski-Szmek 399a2a
index 9f6bd84b21..e4c79415d1 100644
Zbigniew Jędrzejewski-Szmek 399a2a
--- a/src/core/automount.c
Zbigniew Jędrzejewski-Szmek 399a2a
+++ b/src/core/automount.c
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -235,7 +235,7 @@ static void automount_set_state(Automount *a, AutomountState state) {
Zbigniew Jędrzejewski-Szmek 399a2a
         unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], true);
Zbigniew Jędrzejewski-Szmek 399a2a
 }
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-static int automount_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+static int automount_coldplug(Unit *u, Hashmap *deferred_work) {
Zbigniew Jędrzejewski-Szmek 399a2a
         Automount *a = AUTOMOUNT(u);
Zbigniew Jędrzejewski-Szmek 399a2a
         int r;
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
diff --git a/src/core/busname.c b/src/core/busname.c
Zbigniew Jędrzejewski-Szmek 399a2a
index 1d77292f9b..43d7607a30 100644
Zbigniew Jędrzejewski-Szmek 399a2a
--- a/src/core/busname.c
Zbigniew Jędrzejewski-Szmek 399a2a
+++ b/src/core/busname.c
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -335,7 +335,7 @@ static void busname_set_state(BusName *n, BusNameState state) {
Zbigniew Jędrzejewski-Szmek 399a2a
         unit_notify(UNIT(n), state_translation_table[old_state], state_translation_table[state], true);
Zbigniew Jędrzejewski-Szmek 399a2a
 }
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-static int busname_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+static int busname_coldplug(Unit *u, Hashmap *deferred_work) {
Zbigniew Jędrzejewski-Szmek 399a2a
         BusName *n = BUSNAME(u);
Zbigniew Jędrzejewski-Szmek 399a2a
         int r;
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
diff --git a/src/core/device.c b/src/core/device.c
Zbigniew Jędrzejewski-Szmek 399a2a
index 1cc103c290..4ff8827219 100644
Zbigniew Jędrzejewski-Szmek 399a2a
--- a/src/core/device.c
Zbigniew Jędrzejewski-Szmek 399a2a
+++ b/src/core/device.c
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -142,7 +142,7 @@ static void device_set_state(Device *d, DeviceState state) {
Zbigniew Jędrzejewski-Szmek 399a2a
         unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], true);
Zbigniew Jędrzejewski-Szmek 399a2a
 }
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-static int device_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+static int device_coldplug(Unit *u, Hashmap *deferred_work) {
Zbigniew Jędrzejewski-Szmek 399a2a
         Device *d = DEVICE(u);
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
         assert(d);
Zbigniew Jędrzejewski-Szmek 399a2a
diff --git a/src/core/manager.c b/src/core/manager.c
Zbigniew Jędrzejewski-Szmek 399a2a
index bc9b7ec620..203a6a0a1a 100644
Zbigniew Jędrzejewski-Szmek 399a2a
--- a/src/core/manager.c
Zbigniew Jędrzejewski-Szmek 399a2a
+++ b/src/core/manager.c
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -983,7 +983,28 @@ static int manager_coldplug(Manager *m) {
Zbigniew Jędrzejewski-Szmek 399a2a
         Unit *u;
Zbigniew Jędrzejewski-Szmek 399a2a
         char *k;
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-        assert(m);
Zbigniew Jędrzejewski-Szmek 399a2a
+        /*
Zbigniew Jędrzejewski-Szmek 399a2a
+         * Some unit types tend to spawn jobs or check other units' state
Zbigniew Jędrzejewski-Szmek 399a2a
+         * during coldplug. This is wrong because it is undefined whether the
Zbigniew Jędrzejewski-Szmek 399a2a
+         * units in question have been already coldplugged (i. e. their state
Zbigniew Jędrzejewski-Szmek 399a2a
+         * restored). This way, we can easily re-start an already started unit
Zbigniew Jędrzejewski-Szmek 399a2a
+         * or otherwise make a wrong decision based on the unit's state.
Zbigniew Jędrzejewski-Szmek 399a2a
+         *
Zbigniew Jędrzejewski-Szmek 399a2a
+         * Solve this by providing a way for coldplug functions to defer
Zbigniew Jędrzejewski-Szmek 399a2a
+         * such actions until after all units have been coldplugged.
Zbigniew Jędrzejewski-Szmek 399a2a
+         *
Zbigniew Jędrzejewski-Szmek 399a2a
+         * We store Unit* -> int(*)(Unit*).
Zbigniew Jędrzejewski-Szmek 399a2a
+         *
Zbigniew Jędrzejewski-Szmek 399a2a
+         * https://bugs.freedesktop.org/show_bug.cgi?id=88401
Zbigniew Jędrzejewski-Szmek 399a2a
+         */
Zbigniew Jędrzejewski-Szmek 399a2a
+        _cleanup_hashmap_free_ Hashmap *deferred_work = NULL;
Zbigniew Jędrzejewski-Szmek 399a2a
+        int(*proc)(Unit*);
Zbigniew Jędrzejewski-Szmek 399a2a
+
Zbigniew Jędrzejewski-Szmek 399a2a
+        assert(m);
Zbigniew Jędrzejewski-Szmek 399a2a
+
Zbigniew Jędrzejewski-Szmek 399a2a
+        deferred_work = hashmap_new(&trivial_hash_ops);
Zbigniew Jędrzejewski-Szmek 399a2a
+        if (!deferred_work)
Zbigniew Jędrzejewski-Szmek 399a2a
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
         /* Then, let's set up their initial state. */
Zbigniew Jędrzejewski-Szmek 399a2a
         HASHMAP_FOREACH_KEY(u, k, m->units, i) {
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -993,7 +1014,17 @@ static int manager_coldplug(Manager *m) {
Zbigniew Jędrzejewski-Szmek 399a2a
                 if (u->id != k)
Zbigniew Jędrzejewski-Szmek 399a2a
                         continue;
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-                q = unit_coldplug(u);
Zbigniew Jędrzejewski-Szmek 399a2a
+                q = unit_coldplug(u, deferred_work);
Zbigniew Jędrzejewski-Szmek 399a2a
+                if (q < 0)
Zbigniew Jędrzejewski-Szmek 399a2a
+                        r = q;
Zbigniew Jędrzejewski-Szmek 399a2a
+        }
Zbigniew Jędrzejewski-Szmek 399a2a
+
Zbigniew Jędrzejewski-Szmek 399a2a
+        /* After coldplugging and setting up initial state of the units,
Zbigniew Jędrzejewski-Szmek 399a2a
+         * let's perform operations which spawn jobs or query units' state. */
Zbigniew Jędrzejewski-Szmek 399a2a
+        HASHMAP_FOREACH_KEY(proc, u, deferred_work, i) {
Zbigniew Jędrzejewski-Szmek 399a2a
+                int q;
Zbigniew Jędrzejewski-Szmek 399a2a
+
Zbigniew Jędrzejewski-Szmek 399a2a
+                q = proc(u);
Zbigniew Jędrzejewski-Szmek 399a2a
                 if (q < 0)
Zbigniew Jędrzejewski-Szmek 399a2a
                         r = q;
Zbigniew Jędrzejewski-Szmek 399a2a
         }
Zbigniew Jędrzejewski-Szmek 399a2a
diff --git a/src/core/mount.c b/src/core/mount.c
Zbigniew Jędrzejewski-Szmek 399a2a
index c971330af2..3ae0eb4621 100644
Zbigniew Jędrzejewski-Szmek 399a2a
--- a/src/core/mount.c
Zbigniew Jędrzejewski-Szmek 399a2a
+++ b/src/core/mount.c
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -617,7 +617,7 @@ static void mount_set_state(Mount *m, MountState state) {
Zbigniew Jędrzejewski-Szmek 399a2a
         m->reload_result = MOUNT_SUCCESS;
Zbigniew Jędrzejewski-Szmek 399a2a
 }
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-static int mount_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+static int mount_coldplug(Unit *u, Hashmap *deferred_work) {
Zbigniew Jędrzejewski-Szmek 399a2a
         Mount *m = MOUNT(u);
Zbigniew Jędrzejewski-Szmek 399a2a
         MountState new_state = MOUNT_DEAD;
Zbigniew Jędrzejewski-Szmek 399a2a
         int r;
Zbigniew Jędrzejewski-Szmek 399a2a
diff --git a/src/core/path.c b/src/core/path.c
Zbigniew Jędrzejewski-Szmek 399a2a
index e5ea79fec7..51e36fa8be 100644
Zbigniew Jędrzejewski-Szmek 399a2a
--- a/src/core/path.c
Zbigniew Jędrzejewski-Szmek 399a2a
+++ b/src/core/path.c
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -440,7 +440,12 @@ static void path_set_state(Path *p, PathState state) {
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
 static void path_enter_waiting(Path *p, bool initial, bool recheck);
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-static int path_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+static int path_enter_waiting_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+        path_enter_waiting(PATH(u), true, true);
Zbigniew Jędrzejewski-Szmek 399a2a
+        return 0;
Zbigniew Jędrzejewski-Szmek 399a2a
+}
Zbigniew Jędrzejewski-Szmek 399a2a
+
Zbigniew Jędrzejewski-Szmek 399a2a
+static int path_coldplug(Unit *u, Hashmap *deferred_work) {
Zbigniew Jędrzejewski-Szmek 399a2a
         Path *p = PATH(u);
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
         assert(p);
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -449,9 +454,10 @@ static int path_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
         if (p->deserialized_state != p->state) {
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
                 if (p->deserialized_state == PATH_WAITING ||
Zbigniew Jędrzejewski-Szmek 399a2a
-                    p->deserialized_state == PATH_RUNNING)
Zbigniew Jędrzejewski-Szmek 399a2a
-                        path_enter_waiting(p, true, true);
Zbigniew Jędrzejewski-Szmek 399a2a
-                else
Zbigniew Jędrzejewski-Szmek 399a2a
+                    p->deserialized_state == PATH_RUNNING) {
Zbigniew Jędrzejewski-Szmek 399a2a
+                        hashmap_put(deferred_work, u, &path_enter_waiting_coldplug);
Zbigniew Jędrzejewski-Szmek 399a2a
+                        path_set_state(p, PATH_WAITING);
Zbigniew Jędrzejewski-Szmek 399a2a
+                } else
Zbigniew Jędrzejewski-Szmek 399a2a
                         path_set_state(p, p->deserialized_state);
Zbigniew Jędrzejewski-Szmek 399a2a
         }
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
diff --git a/src/core/scope.c b/src/core/scope.c
Zbigniew Jędrzejewski-Szmek 399a2a
index b41db7872c..ae6614fbf0 100644
Zbigniew Jędrzejewski-Szmek 399a2a
--- a/src/core/scope.c
Zbigniew Jędrzejewski-Szmek 399a2a
+++ b/src/core/scope.c
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -173,7 +173,7 @@ static int scope_load(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
         return scope_verify(s);
Zbigniew Jędrzejewski-Szmek 399a2a
 }
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-static int scope_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+static int scope_coldplug(Unit *u, Hashmap *deferred_work) {
Zbigniew Jędrzejewski-Szmek 399a2a
         Scope *s = SCOPE(u);
Zbigniew Jędrzejewski-Szmek 399a2a
         int r;
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
diff --git a/src/core/service.c b/src/core/service.c
Zbigniew Jędrzejewski-Szmek 399a2a
index 15e29be149..7781b4e626 100644
Zbigniew Jędrzejewski-Szmek 399a2a
--- a/src/core/service.c
Zbigniew Jędrzejewski-Szmek 399a2a
+++ b/src/core/service.c
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -879,7 +879,7 @@ static void service_set_state(Service *s, ServiceState state) {
Zbigniew Jędrzejewski-Szmek 399a2a
         s->reload_result = SERVICE_SUCCESS;
Zbigniew Jędrzejewski-Szmek 399a2a
 }
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-static int service_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+static int service_coldplug(Unit *u, Hashmap *deferred_work) {
Zbigniew Jędrzejewski-Szmek 399a2a
         Service *s = SERVICE(u);
Zbigniew Jędrzejewski-Szmek 399a2a
         int r;
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
diff --git a/src/core/slice.c b/src/core/slice.c
Zbigniew Jędrzejewski-Szmek 399a2a
index ae9819d015..61ff9d3314 100644
Zbigniew Jędrzejewski-Szmek 399a2a
--- a/src/core/slice.c
Zbigniew Jędrzejewski-Szmek 399a2a
+++ b/src/core/slice.c
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -153,7 +153,7 @@ static int slice_load(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
         return slice_verify(s);
Zbigniew Jędrzejewski-Szmek 399a2a
 }
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-static int slice_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+static int slice_coldplug(Unit *u, Hashmap *deferred_work) {
Zbigniew Jędrzejewski-Szmek 399a2a
         Slice *t = SLICE(u);
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
         assert(t);
Zbigniew Jędrzejewski-Szmek 399a2a
diff --git a/src/core/snapshot.c b/src/core/snapshot.c
Zbigniew Jędrzejewski-Szmek 399a2a
index b70c3beb60..b1d8448771 100644
Zbigniew Jędrzejewski-Szmek 399a2a
--- a/src/core/snapshot.c
Zbigniew Jędrzejewski-Szmek 399a2a
+++ b/src/core/snapshot.c
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -75,7 +75,7 @@ static int snapshot_load(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
         return 0;
Zbigniew Jędrzejewski-Szmek 399a2a
 }
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-static int snapshot_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+static int snapshot_coldplug(Unit *u, Hashmap *deferred_work) {
Zbigniew Jędrzejewski-Szmek 399a2a
         Snapshot *s = SNAPSHOT(u);
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
         assert(s);
Zbigniew Jędrzejewski-Szmek 399a2a
diff --git a/src/core/socket.c b/src/core/socket.c
Zbigniew Jędrzejewski-Szmek 399a2a
index 88aae4815b..760de0203d 100644
Zbigniew Jędrzejewski-Szmek 399a2a
--- a/src/core/socket.c
Zbigniew Jędrzejewski-Szmek 399a2a
+++ b/src/core/socket.c
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -1326,7 +1326,7 @@ static void socket_set_state(Socket *s, SocketState state) {
Zbigniew Jędrzejewski-Szmek 399a2a
         unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
Zbigniew Jędrzejewski-Szmek 399a2a
 }
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-static int socket_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+static int socket_coldplug(Unit *u, Hashmap *deferred_work) {
Zbigniew Jędrzejewski-Szmek 399a2a
         Socket *s = SOCKET(u);
Zbigniew Jędrzejewski-Szmek 399a2a
         int r;
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
diff --git a/src/core/swap.c b/src/core/swap.c
Zbigniew Jędrzejewski-Szmek 399a2a
index 5c19af5d91..369abf0f53 100644
Zbigniew Jędrzejewski-Szmek 399a2a
--- a/src/core/swap.c
Zbigniew Jędrzejewski-Szmek 399a2a
+++ b/src/core/swap.c
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -513,7 +513,7 @@ static void swap_set_state(Swap *s, SwapState state) {
Zbigniew Jędrzejewski-Szmek 399a2a
                         job_add_to_run_queue(UNIT(other)->job);
Zbigniew Jędrzejewski-Szmek 399a2a
 }
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-static int swap_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+static int swap_coldplug(Unit *u, Hashmap *deferred_work) {
Zbigniew Jędrzejewski-Szmek 399a2a
         Swap *s = SWAP(u);
Zbigniew Jędrzejewski-Szmek 399a2a
         SwapState new_state = SWAP_DEAD;
Zbigniew Jędrzejewski-Szmek 399a2a
         int r;
Zbigniew Jędrzejewski-Szmek 399a2a
diff --git a/src/core/target.c b/src/core/target.c
Zbigniew Jędrzejewski-Szmek 399a2a
index 33fb66bc3f..2411a8e758 100644
Zbigniew Jędrzejewski-Szmek 399a2a
--- a/src/core/target.c
Zbigniew Jędrzejewski-Szmek 399a2a
+++ b/src/core/target.c
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -107,7 +107,7 @@ static int target_load(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
         return 0;
Zbigniew Jędrzejewski-Szmek 399a2a
 }
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-static int target_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+static int target_coldplug(Unit *u, Hashmap *deferred_work) {
Zbigniew Jędrzejewski-Szmek 399a2a
         Target *t = TARGET(u);
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
         assert(t);
Zbigniew Jędrzejewski-Szmek 399a2a
diff --git a/src/core/timer.c b/src/core/timer.c
Zbigniew Jędrzejewski-Szmek 399a2a
index 45744c7de5..48cf9c16a8 100644
Zbigniew Jędrzejewski-Szmek 399a2a
--- a/src/core/timer.c
Zbigniew Jędrzejewski-Szmek 399a2a
+++ b/src/core/timer.c
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -268,7 +268,12 @@ static void timer_set_state(Timer *t, TimerState state) {
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
 static void timer_enter_waiting(Timer *t, bool initial);
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-static int timer_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+static int timer_enter_waiting_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+        timer_enter_waiting(TIMER(u), false);
Zbigniew Jędrzejewski-Szmek 399a2a
+        return 0;
Zbigniew Jędrzejewski-Szmek 399a2a
+}
Zbigniew Jędrzejewski-Szmek 399a2a
+
Zbigniew Jędrzejewski-Szmek 399a2a
+static int timer_coldplug(Unit *u, Hashmap *deferred_work) {
Zbigniew Jędrzejewski-Szmek 399a2a
         Timer *t = TIMER(u);
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
         assert(t);
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -276,9 +281,10 @@ static int timer_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
         if (t->deserialized_state != t->state) {
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-                if (t->deserialized_state == TIMER_WAITING)
Zbigniew Jędrzejewski-Szmek 399a2a
-                        timer_enter_waiting(t, false);
Zbigniew Jędrzejewski-Szmek 399a2a
-                else
Zbigniew Jędrzejewski-Szmek 399a2a
+                if (t->deserialized_state == TIMER_WAITING) {
Zbigniew Jędrzejewski-Szmek 399a2a
+                        hashmap_put(deferred_work, u, &timer_enter_waiting_coldplug);
Zbigniew Jędrzejewski-Szmek 399a2a
+                        timer_set_state(t, TIMER_WAITING);
Zbigniew Jędrzejewski-Szmek 399a2a
+                } else
Zbigniew Jędrzejewski-Szmek 399a2a
                         timer_set_state(t, t->deserialized_state);
Zbigniew Jędrzejewski-Szmek 399a2a
         }
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
diff --git a/src/core/unit.c b/src/core/unit.c
Zbigniew Jędrzejewski-Szmek 399a2a
index a6558ee23b..565455bd63 100644
Zbigniew Jędrzejewski-Szmek 399a2a
--- a/src/core/unit.c
Zbigniew Jędrzejewski-Szmek 399a2a
+++ b/src/core/unit.c
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -2859,27 +2859,34 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) {
Zbigniew Jędrzejewski-Szmek 399a2a
         return 0;
Zbigniew Jędrzejewski-Szmek 399a2a
 }
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-int unit_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+static int unit_add_deserialized_job_coldplug(Unit *u) {
Zbigniew Jędrzejewski-Szmek 399a2a
+        int r;
Zbigniew Jędrzejewski-Szmek 399a2a
+
Zbigniew Jędrzejewski-Szmek 399a2a
+        r = manager_add_job(u->manager, u->deserialized_job, u, JOB_IGNORE_REQUIREMENTS, false, NULL, NULL);
Zbigniew Jędrzejewski-Szmek 399a2a
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 399a2a
+                return r;
Zbigniew Jędrzejewski-Szmek 399a2a
+
Zbigniew Jędrzejewski-Szmek 399a2a
+        u->deserialized_job = _JOB_TYPE_INVALID;
Zbigniew Jędrzejewski-Szmek 399a2a
+
Zbigniew Jędrzejewski-Szmek 399a2a
+        return 0;
Zbigniew Jędrzejewski-Szmek 399a2a
+}
Zbigniew Jędrzejewski-Szmek 399a2a
+
Zbigniew Jędrzejewski-Szmek 399a2a
+int unit_coldplug(Unit *u, Hashmap *deferred_work) {
Zbigniew Jędrzejewski-Szmek 399a2a
         int r;
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
         assert(u);
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
         if (UNIT_VTABLE(u)->coldplug)
Zbigniew Jędrzejewski-Szmek 399a2a
-                if ((r = UNIT_VTABLE(u)->coldplug(u)) < 0)
Zbigniew Jędrzejewski-Szmek 399a2a
+                if ((r = UNIT_VTABLE(u)->coldplug(u, deferred_work)) < 0)
Zbigniew Jędrzejewski-Szmek 399a2a
                         return r;
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
         if (u->job) {
Zbigniew Jędrzejewski-Szmek 399a2a
                 r = job_coldplug(u->job);
Zbigniew Jędrzejewski-Szmek 399a2a
                 if (r < 0)
Zbigniew Jędrzejewski-Szmek 399a2a
                         return r;
Zbigniew Jędrzejewski-Szmek 399a2a
-        } else if (u->deserialized_job >= 0) {
Zbigniew Jędrzejewski-Szmek 399a2a
+        } else if (u->deserialized_job >= 0)
Zbigniew Jędrzejewski-Szmek 399a2a
                 /* legacy */
Zbigniew Jędrzejewski-Szmek 399a2a
-                r = manager_add_job(u->manager, u->deserialized_job, u, JOB_IGNORE_REQUIREMENTS, false, NULL, NULL);
Zbigniew Jędrzejewski-Szmek 399a2a
-                if (r < 0)
Zbigniew Jędrzejewski-Szmek 399a2a
-                        return r;
Zbigniew Jędrzejewski-Szmek 399a2a
-
Zbigniew Jędrzejewski-Szmek 399a2a
-                u->deserialized_job = _JOB_TYPE_INVALID;
Zbigniew Jędrzejewski-Szmek 399a2a
-        }
Zbigniew Jędrzejewski-Szmek 399a2a
+                hashmap_put(deferred_work, u, &unit_add_deserialized_job_coldplug);
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
         return 0;
Zbigniew Jędrzejewski-Szmek 399a2a
 }
Zbigniew Jędrzejewski-Szmek 399a2a
diff --git a/src/core/unit.h b/src/core/unit.h
Zbigniew Jędrzejewski-Szmek 399a2a
index 291bc77a76..7ebc489c80 100644
Zbigniew Jędrzejewski-Szmek 399a2a
--- a/src/core/unit.h
Zbigniew Jędrzejewski-Szmek 399a2a
+++ b/src/core/unit.h
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -307,8 +307,14 @@ struct UnitVTable {
Zbigniew Jędrzejewski-Szmek 399a2a
         int (*load)(Unit *u);
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
         /* If a lot of units got created via enumerate(), this is
Zbigniew Jędrzejewski-Szmek 399a2a
-         * where to actually set the state and call unit_notify(). */
Zbigniew Jędrzejewski-Szmek 399a2a
-        int (*coldplug)(Unit *u);
Zbigniew Jędrzejewski-Szmek 399a2a
+         * where to actually set the state and call unit_notify().
Zbigniew Jędrzejewski-Szmek 399a2a
+         *
Zbigniew Jędrzejewski-Szmek 399a2a
+         * This must not reference other units (maybe implicitly through spawning
Zbigniew Jędrzejewski-Szmek 399a2a
+         * jobs), because it is possible that they are not yet coldplugged.
Zbigniew Jędrzejewski-Szmek 399a2a
+         * Such actions must be deferred until the end of coldplug bу adding
Zbigniew Jędrzejewski-Szmek 399a2a
+         * a "Unit* -> int(*)(Unit*)" entry into the hashmap.
Zbigniew Jędrzejewski-Szmek 399a2a
+         */
Zbigniew Jędrzejewski-Szmek 399a2a
+        int (*coldplug)(Unit *u, Hashmap *deferred_work);
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
         void (*dump)(Unit *u, FILE *f, const char *prefix);
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
@@ -544,7 +550,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds);
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
 int unit_add_node_link(Unit *u, const char *what, bool wants);
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
-int unit_coldplug(Unit *u);
Zbigniew Jędrzejewski-Szmek 399a2a
+int unit_coldplug(Unit *u, Hashmap *deferred_work);
Zbigniew Jędrzejewski-Szmek 399a2a
 
Zbigniew Jędrzejewski-Szmek 399a2a
 void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) _printf_(3, 0);
Zbigniew Jędrzejewski-Szmek 399a2a