aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSylvain Fasel <sylvain@sonatique.net>2024-01-30 14:15:50 +0100
committerTormod Volden <debian.tormod@gmail.com>2024-02-26 23:21:38 +0100
commit51d2c0ffce90916a4950904e12ea091288e8fca5 (patch)
tree9838e50c8ce8e9a38b6906850d5416ff99c6ece2
parent6cf58bac95ff62cd3453cba2c898993b40e1da66 (diff)
downloadlibusb-51d2c0ffce90916a4950904e12ea091288e8fca5.tar.gz
hotplug_exit: Remove parents recursively
Remove all parents with a single child remaining in parent tree. This ensures that no parents of the direct parent of the device being considered are left in the list, when appearing before their child(ren) in the processing order of the context device list cleaning. References #1452 References #1406
-rw-r--r--libusb/hotplug.c33
-rw-r--r--libusb/version_nano.h2
2 files changed, 26 insertions, 9 deletions
diff --git a/libusb/hotplug.c b/libusb/hotplug.c
index 3c64f69..02d332d 100644
--- a/libusb/hotplug.c
+++ b/libusb/hotplug.c
@@ -161,6 +161,27 @@ void usbi_hotplug_init(struct libusb_context *ctx)
usbi_atomic_store(&ctx->hotplug_ready, 1);
}
+static void usbi_recursively_remove_parents(struct libusb_device *dev, struct libusb_device *next_dev)
+{
+ if (dev && dev->parent_dev) {
+ if (usbi_atomic_load(&dev->parent_dev->refcnt) == 1) {
+ /* The parent was processed before this device in the list and
+ * therefore has its ref count already decremented for its own ref.
+ * The only remaining counted ref comes from its remaining single child.
+ * It will thus be released when its child will be released. So we
+ * can remove it from the list. This is safe as parent_dev cannot be
+ * equal to next_dev given that we know at this point that it was
+ * previously seen in the list. */
+ assert (dev->parent_dev != next_dev);
+ if (dev->parent_dev->list.next && dev->parent_dev->list.prev) {
+ list_del(&dev->parent_dev->list);
+ }
+ }
+
+ usbi_recursively_remove_parents(dev->parent_dev, next_dev);
+ }
+}
+
void usbi_hotplug_exit(struct libusb_context *ctx)
{
struct usbi_hotplug_callback *hotplug_cb, *next_cb;
@@ -193,7 +214,7 @@ void usbi_hotplug_exit(struct libusb_context *ctx)
free(msg);
}
- /* free all discovered devices. due to parent references loop until no devices are freed. */
+ /* free all discovered devices */
for_each_device_safe(ctx, dev, next_dev) {
/* remove the device from the usb_devs list only if there are no
* references held, otherwise leave it on the list so that a
@@ -201,13 +222,9 @@ void usbi_hotplug_exit(struct libusb_context *ctx)
if (usbi_atomic_load(&dev->refcnt) == 1) {
list_del(&dev->list);
}
- if (dev->parent_dev && usbi_atomic_load(&dev->parent_dev->refcnt) == 1) {
- /* the parent was before this device in the list and will be released.
- remove it from the list. this is safe as parent_dev can not be
- equal to next_dev. */
- assert (dev->parent_dev != next_dev);
- list_del(&dev->parent_dev->list);
- }
+
+ usbi_recursively_remove_parents(dev, next_dev);
+
libusb_unref_device(dev);
}
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index c309f7b..a6dfedd 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 11884
+#define LIBUSB_NANO 11885