]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: do not wait for event queue being empty on exit
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 5 Apr 2025 17:04:06 +0000 (02:04 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 7 Apr 2025 13:17:16 +0000 (22:17 +0900)
When the manager process is requested to terminate, if a worker process
try to lock a block device and failed, then the worker returns a
TRY_AGAIN notification and the event is requeued. Hence, the event queue
may have pending events even after manager_exit() is called. In such
situation, sd_event_exit() will never called, and udevd will stuck.

This makes, after termination is requested, it only checks whether there
are any events currently being processed.

src/udev/udev-manager.c

index b1faeedab79be411213bc472cb2221915003b268..375bcd55e8d245357eed3466fda54600dc6ff765 100644 (file)
@@ -588,8 +588,9 @@ static int event_queue_start(Manager *manager) {
         int r;
 
         assert(manager);
+        assert(!manager->exit);
 
-        if (!manager->events || manager->exit || manager->stop_exec_queue)
+        if (!manager->events || manager->stop_exec_queue)
                 return 0;
 
         r = event_source_disable(manager->kill_workers_event);
@@ -921,9 +922,29 @@ static int manager_unlink_queue_file(Manager *manager) {
         return 0;
 }
 
+static int on_post_exit(Manager *manager) {
+        assert(manager);
+        assert(manager->exit);
+
+        LIST_FOREACH(event, event, manager->events)
+                if (event->state == EVENT_RUNNING)
+                        return 0; /* There still exist events being processed. */
+
+        (void) manager_unlink_queue_file(manager);
+
+        if (!hashmap_isempty(manager->workers))
+                return 0; /* There still exist running workers. */
+
+        udev_watch_dump();
+        return sd_event_exit(manager->event, 0);
+}
+
 static int on_post(sd_event_source *s, void *userdata) {
         Manager *manager = ASSERT_PTR(userdata);
 
+        if (manager->exit)
+                return on_post_exit(manager);
+
         if (manager->events) {
                 /* Try to process pending events if idle workers exist. Why is this necessary?
                  * When a worker finished an event and became idle, even if there was a pending event,
@@ -940,11 +961,6 @@ static int on_post(sd_event_source *s, void *userdata) {
         if (!hashmap_isempty(manager->workers))
                 return 0; /* There still exist idle workers. */
 
-        if (manager->exit) {
-                udev_watch_dump();
-                return sd_event_exit(manager->event, 0);
-        }
-
         if (manager->cgroup && set_isempty(manager->synthesize_change_child_event_sources))
                 /* cleanup possible left-over processes in our cgroup */
                 (void) cg_kill(manager->cgroup, SIGKILL, CGROUP_IGNORE_SELF, /* set=*/ NULL, /* kill_log= */ NULL, /* userdata= */ NULL);