diff options
author | Chris Dickens <christopher.a.dickens@gmail.com> | 2013-07-20 13:01:41 -0700 |
---|---|---|
committer | Hans de Goede <hdegoede@redhat.com> | 2013-07-30 17:20:39 +0200 |
commit | 6ac8cd3ef3400ae2f061c194d54c0020ccb01607 (patch) | |
tree | 3ce31bf27d691478085367ae5d2f67ef62e83d59 /libusb/os/linux_udev.c | |
parent | d8a33df7dda5ebfae0ffd6272183a546aa99547d (diff) | |
download | libusb-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.c | 63 |
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"); |