From 7b5e31b8cb427cd95a307c607741cc52a39cb951 Mon Sep 17 00:00:00 2001 From: Chris Dickens Date: Wed, 12 Aug 2020 22:34:03 -0700 Subject: [PATCH] core: Optimize check for pending events 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 --- libusb/core.c | 15 ++++++++------- libusb/hotplug.c | 13 +++++++------ libusb/io.c | 53 ++++++++++++++++++++++++++++++--------------------- libusb/libusbi.h | 24 +++++++++++------------ libusb/version_nano.h | 2 +- 5 files changed, 59 insertions(+), 48 deletions(-) diff --git a/libusb/core.c b/libusb/core.c index 692350a..29d767b 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -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); diff --git a/libusb/hotplug.c b/libusb/hotplug.c index 86aeed8..1b654e3 100644 --- a/libusb/hotplug.c +++ b/libusb/hotplug.c @@ -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); } diff --git a/libusb/io.c b/libusb/io.c index 31a586e..3eedf44 100644 --- a/libusb/io.c +++ b/libusb/io.c @@ -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); } diff --git a/libusb/libusbi.h b/libusb/libusbi.h index afeb27a..4a3e9d5 100644 --- a/libusb/libusbi.h +++ b/libusb/libusbi.h @@ -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) diff --git a/libusb/version_nano.h b/libusb/version_nano.h index c972e09..fd49514 100644 --- a/libusb/version_nano.h +++ b/libusb/version_nano.h @@ -1 +1 @@ -#define LIBUSB_NANO 11533 +#define LIBUSB_NANO 11534 -- 2.7.4