core: Optimize check for pending events
authorChris Dickens <christopher.a.dickens@gmail.com>
Thu, 13 Aug 2020 05:34:03 +0000 (22:34 -0700)
committerChris Dickens <christopher.a.dickens@gmail.com>
Thu, 13 Aug 2020 05:34:03 +0000 (22:34 -0700)
Prior to this commit, a check for whether any events are pending
involved checking four different variables within the context. Optimize
this by using multiple bits within a single unsigned integer to
represent all the possible events that could be pending. This reduces
the check for whether any events are pending to a single load.

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 692350a..29d767b 100644 (file)
@@ -1437,8 +1437,8 @@ static void do_close(struct libusb_context *ctx,
 void API_EXPORTED libusb_close(libusb_device_handle *dev_handle)
 {
        struct libusb_context *ctx;
+       unsigned int event_flags;
        int handling_events;
-       int pending_events;
 
        if (!dev_handle)
                return;
@@ -1459,9 +1459,10 @@ void API_EXPORTED libusb_close(libusb_device_handle *dev_handle)
                /* 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)
+               event_flags = ctx->event_flags;
+               if (!ctx->device_close++)
+                       ctx->event_flags |= USBI_EVENT_DEVICE_CLOSE;
+               if (!event_flags)
                        usbi_signal_event(&ctx->event);
                usbi_mutex_unlock(&ctx->event_data_lock);
 
@@ -1476,9 +1477,9 @@ void API_EXPORTED libusb_close(libusb_device_handle *dev_handle)
                /* 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)
+               if (!--ctx->device_close)
+                       ctx->event_flags &= ~USBI_EVENT_DEVICE_CLOSE;
+               if (!ctx->event_flags)
                        usbi_clear_event(&ctx->event);
                usbi_mutex_unlock(&ctx->event_data_lock);
 
index 86aeed8..1b654e3 100644 (file)
@@ -206,8 +206,8 @@ 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;
        struct libusb_hotplug_message *message = calloc(1, sizeof(*message));
+       unsigned int event_flags;
 
        if (!message) {
                usbi_err(ctx, "error allocating hotplug message");
@@ -220,9 +220,10 @@ void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device
        /* 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);
+       event_flags = ctx->event_flags;
+       ctx->event_flags |= USBI_EVENT_HOTPLUG_MSG_PENDING;
        list_add_tail(&message->list, &ctx->hotplug_msgs);
-       if (!pending_events)
+       if (!event_flags)
                usbi_signal_event(&ctx->event);
        usbi_mutex_unlock(&ctx->event_data_lock);
 }
@@ -341,12 +342,12 @@ void API_EXPORTED libusb_hotplug_deregister_callback(libusb_context *ctx,
        usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
 
        if (deregistered) {
-               int pending_events;
+               unsigned int event_flags;
 
                usbi_mutex_lock(&ctx->event_data_lock);
-               pending_events = usbi_pending_events(ctx);
+               event_flags = ctx->event_flags;
                ctx->event_flags |= USBI_EVENT_HOTPLUG_CB_DEREGISTERED;
-               if (!pending_events)
+               if (!event_flags)
                        usbi_signal_event(&ctx->event);
                usbi_mutex_unlock(&ctx->event_data_lock);
        }
index 31a586e..3eedf44 100644 (file)
@@ -1694,12 +1694,13 @@ void usbi_signal_transfer_completion(struct usbi_transfer *itransfer)
 
        if (dev_handle) {
                struct libusb_context *ctx = HANDLE_CTX(dev_handle);
-               int pending_events;
+               unsigned int event_flags;
 
                usbi_mutex_lock(&ctx->event_data_lock);
-               pending_events = usbi_pending_events(ctx);
+               event_flags = ctx->event_flags;
+               ctx->event_flags |= USBI_EVENT_TRANSFER_COMPLETED;
                list_add_tail(&itransfer->completed_list, &ctx->completed_transfers);
-               if (!pending_events)
+               if (!event_flags)
                        usbi_signal_event(&ctx->event);
                usbi_mutex_unlock(&ctx->event_data_lock);
        }
@@ -1877,16 +1878,16 @@ int API_EXPORTED libusb_event_handler_active(libusb_context *ctx)
  */
 void API_EXPORTED libusb_interrupt_event_handler(libusb_context *ctx)
 {
-       int pending_events;
+       unsigned int event_flags;
 
        usbi_dbg(" ");
 
        ctx = usbi_get_context(ctx);
        usbi_mutex_lock(&ctx->event_data_lock);
 
-       pending_events = usbi_pending_events(ctx);
+       event_flags = ctx->event_flags;
        ctx->event_flags |= USBI_EVENT_USER_INTERRUPT;
-       if (!pending_events)
+       if (!event_flags)
                usbi_signal_event(&ctx->event);
 
        usbi_mutex_unlock(&ctx->event_data_lock);
@@ -2059,30 +2060,38 @@ static int handle_event_trigger(struct libusb_context *ctx)
        }
 
        /* check if someone is closing a device */
-       if (ctx->device_close)
+       if (ctx->event_flags & USBI_EVENT_DEVICE_CLOSE)
                usbi_dbg("someone is closing a device");
 
        /* check for any pending hotplug messages */
-       if (!list_empty(&ctx->hotplug_msgs)) {
+       if (ctx->event_flags & USBI_EVENT_HOTPLUG_MSG_PENDING) {
                usbi_dbg("hotplug message received");
+               ctx->event_flags &= ~USBI_EVENT_HOTPLUG_MSG_PENDING;
+               assert(!list_empty(&ctx->hotplug_msgs));
                list_cut(&hotplug_msgs, &ctx->hotplug_msgs);
        }
 
        /* complete any pending transfers */
-       while (r == 0 && !list_empty(&ctx->completed_transfers)) {
-               struct usbi_transfer *itransfer =
-                       list_first_entry(&ctx->completed_transfers, struct usbi_transfer, completed_list);
+       if (ctx->event_flags & USBI_EVENT_TRANSFER_COMPLETED) {
+               assert(!list_empty(&ctx->completed_transfers));
+               while (r == 0 && !list_empty(&ctx->completed_transfers)) {
+                       struct usbi_transfer *itransfer =
+                               list_first_entry(&ctx->completed_transfers, struct usbi_transfer, completed_list);
 
-               list_del(&itransfer->completed_list);
-               usbi_mutex_unlock(&ctx->event_data_lock);
-               r = usbi_backend.handle_transfer_completion(itransfer);
-               if (r)
-                       usbi_err(ctx, "backend handle_transfer_completion failed with error %d", r);
-               usbi_mutex_lock(&ctx->event_data_lock);
+                       list_del(&itransfer->completed_list);
+                       usbi_mutex_unlock(&ctx->event_data_lock);
+                       r = usbi_backend.handle_transfer_completion(itransfer);
+                       if (r)
+                               usbi_err(ctx, "backend handle_transfer_completion failed with error %d", r);
+                       usbi_mutex_lock(&ctx->event_data_lock);
+               }
+
+               if (list_empty(&ctx->completed_transfers))
+                       ctx->event_flags &= ~USBI_EVENT_TRANSFER_COMPLETED;
        }
 
        /* if no further pending events, clear the event */
-       if (!usbi_pending_events(ctx))
+       if (!ctx->event_flags)
                usbi_clear_event(&ctx->event);
 
        usbi_mutex_unlock(&ctx->event_data_lock);
@@ -2159,7 +2168,7 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv)
 
                /* if no further pending events, clear the event so that we do
                 * not immediately return from the wait function */
-               if (!usbi_pending_events(ctx))
+               if (!ctx->event_flags)
                        usbi_clear_event(&ctx->event);
        }
        usbi_mutex_unlock(&ctx->event_data_lock);
@@ -2579,13 +2588,13 @@ void API_EXPORTED libusb_set_pollfd_notifiers(libusb_context *ctx,
  */
 static void usbi_event_source_notification(struct libusb_context *ctx)
 {
-       int pending_events;
+       unsigned int event_flags;
 
        /* Record that there is a new poll fd.
         * Only signal an event if there are no prior pending events. */
-       pending_events = usbi_pending_events(ctx);
+       event_flags = ctx->event_flags;
        ctx->event_flags |= USBI_EVENT_EVENT_SOURCES_MODIFIED;
-       if (!pending_events)
+       if (!event_flags)
                usbi_signal_event(&ctx->event);
 }
 
index afeb27a..4a3e9d5 100644 (file)
@@ -392,6 +392,9 @@ struct libusb_context {
 
 extern struct libusb_context *usbi_default_context;
 
+extern struct list_head active_contexts_list;
+extern usbi_mutex_static_t active_contexts_lock;
+
 static inline struct libusb_context *usbi_get_context(struct libusb_context *ctx)
 {
        return ctx ? ctx : usbi_default_context;
@@ -406,6 +409,15 @@ enum usbi_event_flags {
 
        /* A hotplug callback deregistration is pending */
        USBI_EVENT_HOTPLUG_CB_DEREGISTERED = 1U << 2,
+
+       /* One or more hotplug messages are pending */
+       USBI_EVENT_HOTPLUG_MSG_PENDING = 1U << 3,
+
+       /* One or more completed transfers are pending */
+       USBI_EVENT_TRANSFER_COMPLETED = 1U << 4,
+
+       /* A device is in the process of being closed */
+       USBI_EVENT_DEVICE_CLOSE = 1U << 5,
 };
 
 /* Macros for managing event handling state */
@@ -424,15 +436,6 @@ static inline void usbi_end_event_handling(struct libusb_context *ctx)
        usbi_tls_key_set(ctx->event_handling_key, NULL);
 }
 
-/* Update the following function if new event sources are added */
-static inline int usbi_pending_events(struct libusb_context *ctx)
-{
-       return ctx->event_flags ||
-              ctx->device_close ||
-              !list_empty(&ctx->hotplug_msgs) ||
-              !list_empty(&ctx->completed_transfers);
-}
-
 struct libusb_device {
        /* lock protects refcnt, everything else is finalized at initialization
         * time */
@@ -1286,9 +1289,6 @@ struct usbi_os_backend {
 
 extern const struct usbi_os_backend usbi_backend;
 
-extern struct list_head active_contexts_list;
-extern usbi_mutex_static_t active_contexts_lock;
-
 #define for_each_context(c) \
        for_each_helper(c, &active_contexts_list, struct libusb_context)
 
index c972e09..fd49514 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 11533
+#define LIBUSB_NANO 11534