hotplug: Remove use of pthread_cancel from linux_udev
authorChris Dickens <christopher.a.dickens@gmail.com>
Sat, 20 Jul 2013 20:01:41 +0000 (13:01 -0700)
committerHans de Goede <hdegoede@redhat.com>
Tue, 30 Jul 2013 15:20:39 +0000 (17:20 +0200)
Using pthread_cancel() presents the opportunity for deadlock, so
use a control pipe to cause the event thread to gracefully exit.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
libusb/os/linux_udev.c
libusb/version_nano.h

index 5a2aadf..70f632d 100644 (file)
@@ -46,6 +46,7 @@
 /* udev context */
 static struct udev *udev_ctx = NULL;
 static int udev_monitor_fd = -1;
+static int udev_control_pipe[2] = {-1, -1};
 static struct udev_monitor *udev_monitor = NULL;
 static pthread_t linux_event_thread;
 
@@ -95,14 +96,23 @@ int linux_udev_start_event_monitor(void)
                goto err_free_monitor;
        }
 
+       r = usbi_pipe(udev_control_pipe);
+       if (r) {
+               usbi_err(NULL, "could not create udev control pipe");
+               goto err_free_monitor;
+       }
+
        r = pthread_create(&linux_event_thread, NULL, linux_udev_event_thread_main, NULL);
        if (r) {
                usbi_err(NULL, "creating hotplug event thread (%d)", r);
-               goto err_free_monitor;
+               goto err_close_pipe;
        }
 
        return LIBUSB_SUCCESS;
 
+err_close_pipe:
+       close(udev_control_pipe[0]);
+       close(udev_control_pipe[1]);
 err_free_monitor:
        udev_monitor_unref(udev_monitor);
        udev_monitor = NULL;
@@ -115,14 +125,19 @@ err_free_ctx:
 
 int linux_udev_stop_event_monitor(void)
 {
+       char dummy = 1;
+       int r;
+
        assert(udev_ctx != NULL);
        assert(udev_monitor != NULL);
        assert(udev_monitor_fd != -1);
 
-       /* Cancel the event thread. This is the only way to guarantee the
-          thread exits since closing the monitor fd won't necessarily cause
-          poll to return. */
-       pthread_cancel(linux_event_thread);
+       /* Write some dummy data to the control pipe and
+        * wait for the thread to exit */
+       r = usbi_write(udev_control_pipe[1], &dummy, sizeof(dummy));
+       if (r <= 0) {
+               usbi_warn(NULL, "udev control pipe signal failed");
+       }
        pthread_join(linux_event_thread, NULL);
 
        /* Release the udev monitor */
@@ -134,27 +149,45 @@ int linux_udev_stop_event_monitor(void)
        udev_unref(udev_ctx);
        udev_ctx = NULL;
 
+       /* close and reset control pipe */
+       close(udev_control_pipe[0]);
+       close(udev_control_pipe[1]);
+       udev_control_pipe[0] = -1;
+       udev_control_pipe[1] = -1;
+
        return LIBUSB_SUCCESS;
 }
 
 static void *linux_udev_event_thread_main(void *arg)
 {
+       char dummy;
+       int r;
        struct udev_device* udev_dev;
-       struct pollfd fds = {.fd = udev_monitor_fd,
-                            .events = POLLIN};
+       struct pollfd fds[] = {
+               {.fd = udev_control_pipe[0],
+                .events = POLLIN},
+               {.fd = udev_monitor_fd,
+                .events = POLLIN},
+       };
 
        usbi_dbg("udev event thread entering.");
 
-       while (1 == poll(&fds, 1, -1)) {
-               if (NULL == udev_monitor || POLLIN != fds.revents) {
+       while (poll(fds, 2, -1) >= 0) {
+               if (fds[0].revents & POLLIN) {
+                       /* activity on control pipe, read the byte and exit */
+                       r = usbi_read(udev_control_pipe[0], &dummy, sizeof(dummy));
+                       if (r <= 0) {
+                               usbi_warn(NULL, "udev control pipe read failed");
+                       }
                        break;
                }
-
-               usbi_mutex_static_lock(&linux_hotplug_lock);
-               udev_dev = udev_monitor_receive_device(udev_monitor);
-               if (udev_dev)
-                       udev_hotplug_event(udev_dev);
-               usbi_mutex_static_unlock(&linux_hotplug_lock);
+               if (fds[1].revents & POLLIN) {
+                       usbi_mutex_static_lock(&linux_hotplug_lock);
+                       udev_dev = udev_monitor_receive_device(udev_monitor);
+                       if (udev_dev)
+                               udev_hotplug_event(udev_dev);
+                       usbi_mutex_static_unlock(&linux_hotplug_lock);
+               }
        }
 
        usbi_dbg("udev event thread exiting");
index ca8666b..a684aef 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 10782
+#define LIBUSB_NANO 10783