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.
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;