summaryrefslogtreecommitdiff
path: root/libusb/os/linux_udev.c
diff options
context:
space:
mode:
authorChris Dickens <christopher.a.dickens@gmail.com>2013-07-20 13:01:41 -0700
committerHans de Goede <hdegoede@redhat.com>2013-07-30 17:20:39 +0200
commit6ac8cd3ef3400ae2f061c194d54c0020ccb01607 (patch)
tree3ce31bf27d691478085367ae5d2f67ef62e83d59 /libusb/os/linux_udev.c
parentd8a33df7dda5ebfae0ffd6272183a546aa99547d (diff)
downloadlibusb-6ac8cd3ef3400ae2f061c194d54c0020ccb01607.tar.gz
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>
Diffstat (limited to 'libusb/os/linux_udev.c')
-rw-r--r--libusb/os/linux_udev.c63
1 files changed, 48 insertions, 15 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");