Commit 95e9bb6
committed
uevent-sender: Retry udev_device_new_from_syspath() on failure
Fix a race condition in uevent_sender_send() where
udev_device_new_from_syspath() transiently fails with ENOENT, even
though the device directory exists in the mocked /sys filesystem.
This caused a test flake:
> t_mt_uevent: assertion failed (_data1_->change_count == _data1_->num_changes): (9 == 10)
t_mt_uevent test creates a testbed with a device at /sys/devices/dev1,
then spawns a thread that rapidly sends uevent notifications for that
device by calling tb.uevent(). Each call to tb.uevent() creates a new
UeventSender object, which internally creates a new libudev context via
udev_new(), and then calls udev_device_new_from_syspath() to read the
device information from the mocked /sys filesystem.
The race occurs when multiple threads are simultaneously accessing paths
under the mocked /sys through the umockdev preload library:
1. Thread A (emitter): Calls tb.uevent() → uevent_sender_send() →
udev_device_new_from_syspath(sender->udev, "/sys/devices/dev1")
2. Thread B (main loop or noise): Simultaneously accessing /sys paths
(e.g., reading device attributes, binding sockets)
3. The preload library uses TRAP_PATH_LOCK (a pthread mutex) to
serialize access to trap_path() for all intercepted filesystem
operations under /sys, /dev, and /proc.
4. When libudev's udev_device_new_from_syspath() tries to enumerate the
device, it performs multiple filesystem operations (stat, readdir,
readlink, etc.) on /sys/devices/dev1 and its subdirectories.
5. If another thread holds TRAP_PATH_LOCK during a critical part of
libudev's device enumeration, or if libudev's internal caching/state
is inconsistent due to concurrent access, udev_device_new_from_syspath()
returns NULL with errno=ENOENT.
6. Importantly, the failure is **transient** - an immediate retry
succeeds because the device directory hasn't actually disappeared; it
was just temporarily inaccessible or libudev's state was inconsistent.
This is a pragmatic workaround rather than a proper fix.1 parent 79bfb6a commit 95e9bb6
3 files changed
Lines changed: 11 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
247 | 247 | | |
248 | 248 | | |
249 | 249 | | |
250 | | - | |
251 | | - | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
252 | 259 | | |
253 | 260 | | |
254 | 261 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1008 | 1008 | | |
1009 | 1009 | | |
1010 | 1010 | | |
1011 | | - | |
| 1011 | + | |
1012 | 1012 | | |
1013 | 1013 | | |
1014 | 1014 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
811 | 811 | | |
812 | 812 | | |
813 | 813 | | |
814 | | - | |
| 814 | + | |
815 | 815 | | |
816 | 816 | | |
817 | 817 | | |
| |||
0 commit comments