From 852efb5a3e82de43cf6288e9904cb254ff636aa0 Mon Sep 17 00:00:00 2001
From: Chris Dickens <christopher.a.dickens@gmail.com>
Date: Sat, 20 Jul 2013 13:01:41 -0700
Subject: [PATCH 2/2] hotplug: Remove use of pthread_cancel from linux_udev
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 | 63 ++++++++++++++++++++++++++++++++++++++------------
libusb/version_nano.h | 2 +-
2 files changed, 49 insertions(+), 16 deletions(-)
diff --git a/libusb/os/linux_udev.c b/libusb/os/linux_udev.c
index 5a2aadf..70f632d 100644
--- a/libusb/os/linux_udev.c
+++ b/libusb/os/linux_udev.c
@@ -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");
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index 34e26ff..39ad7e3 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 10777
+#define LIBUSB_NANO 10778
--
1.8.3.1