core: Signal the event pipe at most one time
authorChris Dickens <christopher.a.dickens@gmail.com>
Tue, 18 Nov 2014 07:53:14 +0000 (23:53 -0800)
committerChris Dickens <chris.dickens@hp.com>
Fri, 19 Dec 2014 19:22:03 +0000 (11:22 -0800)
This change will ensure that the event pipe is only signalled
at most one time during the course of any incoming events. New
events that occur while the event pipe is in the signalled state
will not cause the event pipe to be signalled again.

This change will provide support for the use of native events on
the Windows/WinCE backends, as these events are binary and do not
"count" the number of times they are signalled.

Signed-off-by: Chris Dickens <christopher.a.dickens@gmail.com>
libusb/core.c
libusb/hotplug.c
libusb/io.c
libusb/libusbi.h
libusb/version_nano.h

index 626fabc..9894209 100644 (file)
@@ -1200,13 +1200,16 @@ int usbi_clear_event(struct libusb_context *ctx)
  */
 void usbi_fd_notification(struct libusb_context *ctx)
 {
-       /* record that there is a new poll fd */
+       int pending_events;
+
+       /* Record that there is a new poll fd.
+        * Only signal an event if there are no prior pending events. */
        usbi_mutex_lock(&ctx->event_data_lock);
+       pending_events = usbi_pending_events(ctx);
        ctx->fd_notify = 1;
+       if (!pending_events)
+               usbi_signal_event(ctx);
        usbi_mutex_unlock(&ctx->event_data_lock);
-
-       /* signal the event pipe to interrupt event handlers */
-       usbi_signal_event(ctx);
 }
 
 /** \ingroup dev
@@ -1407,6 +1410,7 @@ static void do_close(struct libusb_context *ctx,
 void API_EXPORTED libusb_close(libusb_device_handle *dev_handle)
 {
        struct libusb_context *ctx;
+       int pending_events;
 
        if (!dev_handle)
                return;
@@ -1420,26 +1424,28 @@ void API_EXPORTED libusb_close(libusb_device_handle *dev_handle)
         * thread from doing event handling) because we will be removing a file
         * descriptor from the polling loop. */
 
-       /* record that we are closing a device */
+       /* Record that we are closing a device.
+        * Only signal an event if there are no prior pending events. */
        usbi_mutex_lock(&ctx->event_data_lock);
+       pending_events = usbi_pending_events(ctx);
        ctx->device_close++;
+       if (!pending_events)
+               usbi_signal_event(ctx);
        usbi_mutex_unlock(&ctx->event_data_lock);
 
-       /* signal the event pipe to interrupt event handlers */
-       usbi_signal_event(ctx);
-
        /* take event handling lock */
        libusb_lock_events(ctx);
 
-       /* clear the event pipe */
-       usbi_clear_event(ctx);
-
        /* Close the device */
        do_close(ctx, dev_handle);
 
-       /* we're done with closing this device */
+       /* We're done with closing this device.
+        * Clear the event pipe if there are no further pending events. */
        usbi_mutex_lock(&ctx->event_data_lock);
        ctx->device_close--;
+       pending_events = usbi_pending_events(ctx);
+       if (!pending_events)
+               usbi_clear_event(ctx);
        usbi_mutex_unlock(&ctx->event_data_lock);
 
        /* Release event handling lock and wake up event waiters */
index 9171f2c..3ac159f 100644 (file)
@@ -206,6 +206,7 @@ void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
 void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
        libusb_hotplug_event event)
 {
+       int pending_events;;
        libusb_hotplug_message *message = calloc(1, sizeof(*message));
 
        if (!message) {
@@ -216,12 +217,14 @@ void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device
        message->event = event;
        message->device = dev;
 
-       /* Take the event data lock and add this message to the list. */
+       /* Take the event data lock and add this message to the list.
+        * Only signal an event if there are no prior pending events. */
        usbi_mutex_lock(&ctx->event_data_lock);
+       pending_events = usbi_pending_events(ctx);
        list_add_tail(&message->list, &ctx->hotplug_msgs);
+       if (!pending_events)
+               usbi_signal_event(ctx);
        usbi_mutex_unlock(&ctx->event_data_lock);
-
-       usbi_signal_event(ctx);
 }
 
 int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
index 6ae4f8d..a5fda69 100644 (file)
@@ -2029,7 +2029,6 @@ redo_poll:
 
        /* fds[0] is always the event pipe */
        if (fds[0].revents) {
-               unsigned int ru;
                libusb_hotplug_message *message = NULL;
 
                usbi_dbg("caught a fish on the event pipe");
@@ -2037,8 +2036,15 @@ redo_poll:
                /* take the the event data lock while processing events */
                usbi_mutex_lock(&ctx->event_data_lock);
 
+               /* check if someone added a new poll fd */
+               if (ctx->fd_notify) {
+                       usbi_dbg("someone updated the poll fds");
+                       ctx->fd_notify = 0;
+               }
+
                /* check if someone is closing a device */
-               ru = ctx->device_close;
+               if (ctx->device_close)
+                       usbi_dbg("someone is closing a device");
 
                /* check for any pending hotplug messages */
                if (!list_empty(&ctx->hotplug_msgs)) {
@@ -2048,6 +2054,10 @@ redo_poll:
                        list_del(&message->list);
                }
 
+               /* if no further pending events, clear the event pipe */
+               if (!usbi_pending_events(ctx))
+                       usbi_clear_event(ctx);
+
                usbi_mutex_unlock(&ctx->event_data_lock);
 
                /* process the hotplug message, if any */
@@ -2061,13 +2071,6 @@ redo_poll:
                        free(message);
                }
 
-               /* clear the event pipe if this was an fd or hotplug notification */
-               if (!ru || message) {
-                       r = usbi_clear_event(ctx);
-                       if (r)
-                               goto handled;
-               }
-
                if (0 == --r)
                        goto handled;
        }
index c94c5a2..2d2f71a 100644 (file)
@@ -317,6 +317,10 @@ struct libusb_context {
        struct list_head list;
 };
 
+/* Update the following macro if new event sources are added */
+#define usbi_pending_events(ctx) \
+       ((ctx)->device_close || (ctx)->fd_notify || !list_empty(&(ctx)->hotplug_msgs))
+
 #ifdef USBI_TIMERFD_AVAILABLE
 #define usbi_using_timerfd(ctx) ((ctx)->timerfd >= 0)
 #else
index 90d77c5..dad6c22 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 10938
+#define LIBUSB_NANO 10939