From 5a7d8dac9042e5db6f2e058f20d7344ad322d17a Mon Sep 17 00:00:00 2001 From: Daniel Elstner Date: Sat, 5 Sep 2015 03:56:16 +0200 Subject: [PATCH] USB: Handle the case of a callback removing its event source --- src/session.c | 8 +++++- src/usb.c | 69 +++++++++++++++++++++++++++------------------------ 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/src/session.c b/src/session.c index 276f328a..44aa59ed 100644 --- a/src/session.c +++ b/src/session.c @@ -507,7 +507,7 @@ static int sr_session_iteration(struct sr_session *session) due = source->due; #if HAVE_LIBUSB_1_0 && !defined(G_OS_WIN32) - if (usb_due < due && source->poll_object + if (usb_due < due && poll_object == (gintptr)session->ctx->libusb_ctx) due = usb_due; #endif @@ -1077,6 +1077,12 @@ SR_PRIV int sr_session_source_remove_internal(struct sr_session *session, g_array_remove_range(session->pollfds, fd_index, source->num_fds); g_array_remove_index(session->sources, i); + /* + * This is a bit of a hack. To be removed when + * porting over to the GLib main loop. + */ + if (poll_object == (gintptr)session->ctx->libusb_ctx) + session->ctx->usb_source_present = FALSE; return SR_OK; } fd_index += source->num_fds; diff --git a/src/usb.c b/src/usb.c index be4eedcf..fd8bb7a3 100644 --- a/src/usb.c +++ b/src/usb.c @@ -200,33 +200,48 @@ static int usb_callback(int fd, int revents, void *cb_data) ctx = cb_data; start_time = g_get_monotonic_time(); - due = ctx->usb_due; - timeout = MAX(due - start_time, 0); - tv.tv_sec = timeout / G_USEC_PER_SEC; - tv.tv_usec = timeout % G_USEC_PER_SEC; - sr_spew("libusb_handle_events enter, timeout %g ms", 1e-3 * timeout); + if (due > start_time) { + timeout = due - start_time; + tv.tv_sec = timeout / G_USEC_PER_SEC; + tv.tv_usec = timeout % G_USEC_PER_SEC; - ret = libusb_handle_events_timeout_completed(ctx->libusb_ctx, - (ctx->usb_timeout < 0) ? NULL : &tv, NULL); - if (ret != 0) { - /* Warn but still invoke the callback, to give the driver - * a chance to deal with the problem. + sr_spew("libusb_handle_events enter: %g ms timeout", + 1e-3 * timeout); + + ret = libusb_handle_events_timeout_completed(ctx->libusb_ctx, + (ctx->usb_timeout < 0) ? NULL : &tv, NULL); + if (ret != 0) { + /* Warn but still invoke the callback, to give + * the driver a chance to deal with the problem. + */ + sr_warn("Error handling libusb event (%s)", + libusb_error_name(ret)); + } + stop_time = g_get_monotonic_time(); + + sr_spew("libusb_handle_events leave: %g ms elapsed", + 1e-3 * (stop_time - start_time)); + /* + * The event source may have been removed by the driver's + * libusb transfer callback. Skip the callback in that case. */ - sr_warn("Error handling libusb event (%s)", - libusb_error_name(ret)); - } - stop_time = g_get_monotonic_time(); + if (!ctx->usb_source_present) + return TRUE; + } else { + /* Timeout already expired on entry. + */ + stop_time = start_time; - sr_spew("libusb_handle_events leave, %g ms elapsed", - 1e-3 * (stop_time - start_time)); + sr_spew("libusb_handle_events skipped"); + } if (ctx->usb_timeout >= 0) ctx->usb_due = stop_time + ctx->usb_timeout; /* - * Run registered callback to execute any follow-up activity - * to libusb event handling. + * Run the registered callback to execute any follow-up activity + * to libusb's event handling. */ return ctx->usb_cb(-1, (stop_time < due) ? G_IO_IN : 0, ctx->usb_cb_data); @@ -259,7 +274,8 @@ SR_PRIV int usb_source_add(struct sr_session *session, struct sr_context *ctx, * This will have to do for now, until we implement a proper way to * deal with libusb events on Windows. */ - ret = sr_session_source_add(session, -2, 0, 0, &usb_callback, ctx); + ret = sr_session_source_add_internal(session, NULL, 0, + 0, &usb_callback, ctx, (gintptr)ctx->libusb_ctx); #else const struct libusb_pollfd **lupfd; GPollFD *pollfds; @@ -293,21 +309,8 @@ SR_PRIV int usb_source_add(struct sr_session *session, struct sr_context *ctx, SR_PRIV int usb_source_remove(struct sr_session *session, struct sr_context *ctx) { - int ret; - - if (!ctx->usb_source_present) - return SR_OK; - -#ifdef G_OS_WIN32 - /* Remove our idle source */ - sr_session_source_remove(session, -2); -#else - ret = sr_session_source_remove_internal(session, + return sr_session_source_remove_internal(session, (gintptr)ctx->libusb_ctx); -#endif - ctx->usb_source_present = FALSE; - - return ret; } SR_PRIV int usb_get_port_path(libusb_device *dev, char *path, int path_len)