Using one lock for this is a bad idea, as we should not be holding any
locks used by the hotplug thread when trying to stop otherwise the stop
function may wait indefinetely in pthread_join, while the event-thread
is waiting for the lock the caller of the stop function holds.

Using 2 separate locks for this should fix this deadlock, which has been
reported here: https://bugzilla.redhat.com/show_bug.cgi?id=985484

Many thanks to Chris Dickens for figuring out the cause of this deadlock!

CC: Chris Dickens <christopher.a.dick...@gmail.com>
Signed-off-by: Hans de Goede <hdego...@redhat.com>
---
 libusb/os/linux_netlink.c |  5 ++++-
 libusb/os/linux_udev.c    |  7 +++++++
 libusb/os/linux_usbfs.c   | 12 ++++++------
 libusb/os/linux_usbfs.h   |  2 --
 libusb/version_nano.h     |  2 +-
 5 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/libusb/os/linux_netlink.c b/libusb/os/linux_netlink.c
index 3a68f69..dc1ab96 100644
--- a/libusb/os/linux_netlink.c
+++ b/libusb/os/linux_netlink.c
@@ -47,7 +47,10 @@ static pthread_t libusb_linux_event_thread;
 
 static void *linux_netlink_event_thread_main(void *arg);
 
-struct sockaddr_nl snl = { .nl_family=AF_NETLINK, .nl_groups=KERNEL };
+static struct sockaddr_nl snl = { .nl_family=AF_NETLINK, .nl_groups=KERNEL };
+
+/* Serialize event-thread and poll */
+static usbi_mutex_static_t linux_hotplug_lock = USBI_MUTEX_INITIALIZER;
 
 int linux_netlink_start_event_monitor(void)
 {
diff --git a/libusb/os/linux_udev.c b/libusb/os/linux_udev.c
index 5a2aadf..f4edc10 100644
--- a/libusb/os/linux_udev.c
+++ b/libusb/os/linux_udev.c
@@ -52,6 +52,9 @@ static pthread_t linux_event_thread;
 static void udev_hotplug_event(struct udev_device* udev_dev);
 static void *linux_udev_event_thread_main(void *arg);
 
+/* Serialize scan-devices, event-thread, and poll */
+static usbi_mutex_static_t linux_hotplug_lock = USBI_MUTEX_INITIALIZER;
+
 int linux_udev_start_event_monitor(void)
 {
        int r;
@@ -226,6 +229,8 @@ int linux_udev_scan_devices(struct libusb_context *ctx)
 
        assert(udev_ctx != NULL);
 
+       usbi_mutex_static_lock(&linux_hotplug_lock);
+
        enumerator = udev_enumerate_new(udev_ctx);
        if (NULL == enumerator) {
                usbi_err(ctx, "error creating udev enumerator");
@@ -254,6 +259,8 @@ int linux_udev_scan_devices(struct libusb_context *ctx)
 
        udev_enumerate_unref(enumerator);
 
+       usbi_mutex_static_unlock(&linux_hotplug_lock);
+
        return LIBUSB_SUCCESS;
 }
 
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index 09288af..1cdb1aa 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -120,8 +120,8 @@ static int sysfs_has_descriptors = -1;
 /* how many times have we initted (and not exited) ? */
 static volatile int init_count = 0;
 
-/* Serialize hotplug start/stop, scan-devices, event-thread, and poll */
-usbi_mutex_static_t linux_hotplug_lock = USBI_MUTEX_INITIALIZER;
+/* Serialize hotplug start/stop */
+usbi_mutex_static_t linux_hotplug_startstop_lock = USBI_MUTEX_INITIALIZER;
 
 static int linux_start_event_monitor(void);
 static int linux_stop_event_monitor(void);
@@ -419,7 +419,7 @@ static int op_init(struct libusb_context *ctx)
        if (sysfs_has_descriptors)
                usbi_dbg("sysfs has complete descriptors");
 
-       usbi_mutex_static_lock(&linux_hotplug_lock);
+       usbi_mutex_static_lock(&linux_hotplug_startstop_lock);
        r = LIBUSB_SUCCESS;
        if (init_count == 0) {
                /* start up hotplug event handler */
@@ -433,20 +433,20 @@ static int op_init(struct libusb_context *ctx)
                        linux_stop_event_monitor();
        } else
                usbi_err(ctx, "error starting hotplug event monitor");
-       usbi_mutex_static_unlock(&linux_hotplug_lock);
+       usbi_mutex_static_unlock(&linux_hotplug_startstop_lock);
 
        return r;
 }
 
 static void op_exit(void)
 {
-       usbi_mutex_static_lock(&linux_hotplug_lock);
+       usbi_mutex_static_lock(&linux_hotplug_startstop_lock);
        assert(init_count != 0);
        if (!--init_count) {
                /* tear down event handler */
                (void)linux_stop_event_monitor();
        }
-       usbi_mutex_static_unlock(&linux_hotplug_lock);
+       usbi_mutex_static_unlock(&linux_hotplug_startstop_lock);
 }
 
 static int linux_start_event_monitor(void)
diff --git a/libusb/os/linux_usbfs.h b/libusb/os/linux_usbfs.h
index 499bab7..17e6e0f 100644
--- a/libusb/os/linux_usbfs.h
+++ b/libusb/os/linux_usbfs.h
@@ -156,8 +156,6 @@ struct usbfs_disconnect_claim {
 #define IOCTL_USBFS_GET_CAPABILITIES   _IOR('U', 26, __u32)
 #define IOCTL_USBFS_DISCONNECT_CLAIM   _IOR('U', 27, struct 
usbfs_disconnect_claim)
 
-extern usbi_mutex_static_t linux_hotplug_lock;
-
 #if defined(HAVE_LIBUDEV)
 int linux_udev_start_event_monitor(void);
 int linux_udev_stop_event_monitor(void);
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index 525cd7d..f84521e 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 10774
+#define LIBUSB_NANO 10775
-- 
1.8.3.1


------------------------------------------------------------------------------
See everything from the browser to the database with AppDynamics
Get end-to-end visibility with application monitoring from AppDynamics
Isolate bottlenecks and diagnose root cause in seconds.
Start your free trial of AppDynamics Pro today!
http://pubads.g.doubleclick.net/gampad/clk?id=48808831&iu=/4140/ostg.clktrk
_______________________________________________
libusbx-devel mailing list
libusbx-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/libusbx-devel

Reply via email to