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 <christopher.a.dickens@gmail.com>
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 @@
 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 @@
 		/* 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 @@
 		/* 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_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 @@
 	/* 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 @@
 	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 @@
 
 	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 @@
  */
 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 @@
 	}
 
 	/* 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 @@
 
 		/* 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 @@
  */
 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 @@
 
 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 @@
 
 	/* 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 @@
 	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 @@
 
 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