From 9c02ccc0fe2aef5c36f3b2a9fb2b496ece10b7cd 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] 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.
---
 libusb/os/linux_udev.c | 54 +++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 45 insertions(+), 9 deletions(-)

diff --git a/libusb/os/linux_udev.c b/libusb/os/linux_udev.c
index 5a2aadf..8e0a293 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,19 +149,40 @@ 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 (1 == poll(fds, sizeof(fds), -1)) {
+		if (POLLIN == fds[0].revents) {
+			/* 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;
+		}
+
+		if (NULL == udev_monitor || POLLIN != fds[1].revents) {
 			break;
 		}
 
-- 
1.8.1.1

