]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: don't assert on worker cap after killing a broken idle worker
authorDaan De Meyer <daan@amutable.com>
Tue, 21 Apr 2026 08:44:44 +0000 (08:44 +0000)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 27 Apr 2026 19:14:08 +0000 (21:14 +0200)
manager_can_process_event() considers an event processable if either
there is room below children_max to spawn, or an idle worker exists.
When only the latter holds, event_run() picks the idle worker and
tries device_monitor_send(). If that send fails, event_run() SIGKILLs
the worker, marks it WORKER_KILLED and continues the loop. With no
other idle worker available, it falls through to worker_spawn(),
guarded by:

    assert(hashmap_size(manager->workers) < manager->config.children_max);

The just-killed worker is still in manager->workers until its SIGCHLD
is reaped by on_worker_exit(), so at the cap this assertion trips and
udevd aborts:

    Assertion 'hashmap_size(manager->workers) < manager->config.children_max'
    failed at src/udev/udev-manager.c:635, function event_run(). Aborting.

Instead of asserting, bail out when we are already at the worker
limit. The event remains in EVENT_QUEUED; once the killed worker's
SIGCHLD arrives and frees it from the hashmap, on_post() re-runs
event_queue_start() and the event is retried.

src/udev/udev-manager.c

index 46c8a85d98608d03bbd2612df08d29a769008705..7c2530f17fec8b2e5e7c3e801c02b0b1b11d07e3 100644 (file)
@@ -631,8 +631,13 @@ static int event_run(Event *event) {
                 return 0;
         }
 
+        /* No idle worker could accept the event. If we already reached the worker limit, e.g. because
+         * we just killed the only idle worker above, leave the event queued and wait for SIGCHLD of an
+         * exiting worker to free up a slot. on_post() will retry processing the queue. */
+        if (hashmap_size(manager->workers) >= manager->config.children_max)
+                return 0;
+
         /* 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;