]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: check earlier if there is a free room for processing an event
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 5 May 2025 16:37:16 +0000 (01:37 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 9 May 2025 15:26:32 +0000 (00:26 +0900)
src/udev/udev-manager.c

index bfe1a978ab1e234388c600f47109775c0c28d94b..b18d52181ff1ace018c27d9413a2352a522ba705 100644 (file)
@@ -538,19 +538,14 @@ static int worker_spawn(Manager *manager, Event *event) {
 }
 
 static int event_run(Event *event) {
-        static bool log_children_max_reached = true;
-        Manager *manager;
-        Worker *worker;
+        Manager *manager = ASSERT_PTR(ASSERT_PTR(event)->manager);
         int r;
 
-        assert(event);
-        assert(event->manager);
-
         log_device_uevent(event->dev, "Device ready for processing");
 
         (void) event_source_disable(event->retry_event_source);
 
-        manager = event->manager;
+        Worker *worker;
         HASHMAP_FOREACH(worker, manager->workers) {
                 if (worker->state != WORKER_IDLE)
                         continue;
@@ -564,28 +559,16 @@ static int event_run(Event *event) {
                         continue;
                 }
                 worker_attach_event(worker, event);
-                return 1; /* event is now processing. */
-        }
-
-        if (hashmap_size(manager->workers) >= manager->config.children_max) {
-                /* Avoid spamming the debug logs if the limit is already reached and
-                 * many events still need to be processed */
-                if (log_children_max_reached && manager->config.children_max > 1) {
-                        log_debug("Maximum number (%u) of children reached.", hashmap_size(manager->workers));
-                        log_children_max_reached = false;
-                }
-                return 0; /* no free worker */
+                return 0;
         }
 
-        /* Re-enable the debug message for the next batch of events */
-        log_children_max_reached = true;
-
         /* start new worker and pass initial device */
+        assert(hashmap_size(manager->workers) < manager->config.children_max);
         r = worker_spawn(manager, event);
         if (r < 0)
                 return r;
 
-        return 1; /* event is now processing. */
+        return 0;
 }
 
 bool devpath_conflict(const char *a, const char *b) {
@@ -682,6 +665,36 @@ no_blocker:
         return false;
 }
 
+static bool manager_can_process_event(Manager *manager) {
+        static bool children_max_reached_logged = false;
+
+        assert(manager);
+
+        /* Check if there is a free room for processing an event. */
+
+        if (hashmap_size(manager->workers) < manager->config.children_max)
+                goto yes_we_can; /* new worker can be spawned */
+
+        Worker *worker;
+        HASHMAP_FOREACH(worker, manager->workers)
+                if (worker->state == WORKER_IDLE)
+                        goto yes_we_can; /* found an idle worker */
+
+        /* Avoid spamming the debug logs if the limit is already reached and
+         * many events still need to be processed */
+        if (!children_max_reached_logged) {
+                log_debug("Maximum number (%u) of children reached.", hashmap_size(manager->workers));
+                children_max_reached_logged = true;
+        }
+
+        return false;
+
+yes_we_can:
+        /* Re-enable the debug message for the next batch of events */
+        children_max_reached_logged = false;
+        return true;
+}
+
 static int event_queue_start(Manager *manager) {
         int r;
 
@@ -697,6 +710,11 @@ static int event_queue_start(Manager *manager) {
 
         manager_reload(manager, /* force = */ false);
 
+        /* manager_reload() may kill idle workers, hence we may not be possible to start processing an event.
+         * Let's check that and return earlier if we cannot. */
+        if (!manager_can_process_event(manager))
+                return 0;
+
         LIST_FOREACH(event, event, manager->events) {
                 if (event->state != EVENT_QUEUED)
                         continue;
@@ -713,8 +731,12 @@ static int event_queue_start(Manager *manager) {
                                                  strna(device_action_to_string(event->action)));
 
                 r = event_run(event);
-                if (r <= 0) /* 0 means there are no idle workers. Let's escape from the loop. */
+                if (r < 0)
                         return r;
+
+                /* A worker is activated now. Let's check if we can process more events. */
+                if (!manager_can_process_event(manager))
+                        break;
         }
 
         return 0;