Repository navigation
fix: Resolve boot time races and deadlocks - #750
Merged
Merged
Conversation
The startup re-trigger makes udev reprocess every input device. During that window the udev enumerator reports devices whose database entry is incomplete. Only the five properties synthesized from sysfs (SUBSYSTEM, MINOR, DEVNAME, DEVPATH, MAJOR) are present, so the device fails to match its config. Discovery only runs once at startup, so the device was lost for the whole session. Prior to this change initialization checks were only done in the inotify branch. UdevDevice now tracks whether udev finished initializing it. The manager parks not-yet-initialized devices instead of matching them and rechecks them every second until udev is done or the device is removed. Rechecking is only scheduled while devices are parked. A local LLM model was used for root cause analysis and brainstorming. Assisted-by: LLM
Currently the Manager records a config entry immediately after a fire and forget SourceDeviceAdded CompositeCommand. Since the add runs asynchronously inside the composite and Manager cannot await the result without risking a deadlock, the possibility exists for a device to fail to run and the Manager is not informed of the failure. When the add fails the entry stays recorded for the whole session. For devices with unique: true set in their config, a later device matching the same entry then fails the uniqueness check and forks a duplicate composite device. Examples include a gamepad who's js device matches its evdev, or a numlock LED matches the keyboard entry. In both cases the device will fail to find a driver, error, and be dropped from the CompositeDevice, but the Manager will still record the unique slot filled. Add an mpsc transmit channel to the CompositeDevice. This channel must never be awaited or it can cause deadlocks, so use try_send only. On add failure, report the failed device to the Manager, which removes the device id from the source devices and source devices used maps. A local LLM model was used for root cause analysis and brainstorming. Assisted-by: LLM
…tion In the CompositeDevice run loop, set_target_devices() is run, joining on CreateTargetDevice responses from the Manager. During the boot device flood the Manager's channel saturates and the run loop sits blocked for the whole creation window. Any commands queued behind it, such as GetName/GetCapabilities when the DBus interface starts, hit the client's 5 second timeout, log POSSIBLE DEADLOCK, and drop their receivers, producing late SendError responses. External clients, such as steamos-manager, that read CompositeDevice properties immediately after a composite appears observe the failure. Move the stop and create work out of the run loop into a spawned task. Old targets are removed from the routing maps synchronously, then stopped in the background. A target_creation_in_progress flag is set at spawn time so the set_target_devices re-queue guard still covers the async window until the created target paths are known. The spawned task reports completion with a new TargetDevicesCreated command, which clears the flag and enqueues the created paths for attachment. While the guard is active, incoming set_target_devices() are deferred in pending_set_devices, last request wins, and executed once when the guard clears. A local LLM model was used for root cause analysis and brainstorming. Assisted-by: LLM
pastaq
force-pushed
the
pastaq/udev-init-race
branch
from
October 10, 2026 04:11
dd698e6 to
4072b94
Compare
Contributor
|
🎉 This PR is included in version 0.82.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This solves 3 similar issues during startup with races that either cause deadlocks or unexpected behavior.
Patch 1: During udevadm trigger udev devices appear to the manager udev watcher prior to is_initialized being set to true. This was only being checking in the inotify path, which doesn't fire during retrigger, so partially initialized devices would get processed. These devices lacked udev properties (such as ID_INPUT_JOYSTICK) so they would silently fail to re-attach after the hide and would result in loss of input devices on the CompositeDevice
Patch 2: During startup device nodes with identical properties race (i.e. eventX/jsY from the same input device). Whichever device lands first would be sent to the CompositeDevice. Devices without a driver would then fail to add, but Manager is "fire and forget" to avoid deadlocks, so the failure was never reported and the failed device lived forever in source_devices_used. If the entry was unique and an invalid device landed first it would then hold the unique entry. When the valid entry then hits it cannot be added and would spawn a new CompositeDevice.
Patch 3: TargetDevice creation loop is blocking, causing external client commands run at CompositeDevice detection to deadlock and fail.
Closes: #725