summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGraeme Gill <graeme2@argyllcms.com>2010-09-21 10:57:06 +0200
committerPete Batard <pbatard@gmail.com>2010-09-21 10:51:38 +0100
commitb9823a693ee6fb5755ce00b970420e761c1a55fa (patch)
tree71d5ce1474d2f78d944aa3e1bd936d5ed65e9e8e
parent6bbdc6128f31af878826e3530bdfa999e6120741 (diff)
downloadlibusb-b9823a693ee6fb5755ce00b970420e761c1a55fa.tar.gz
Fix a race condition
See http://sourceforge.net/mailarchive/message.php?msg_name=4BD920C5.3090909%40argyllcms.com
-rw-r--r--libusb/io.c49
-rw-r--r--libusb/libusb.h3
-rw-r--r--libusb/sync.c8
3 files changed, 42 insertions, 18 deletions
diff --git a/libusb/io.c b/libusb/io.c
index ab37e8d..7ab811e 100644
--- a/libusb/io.c
+++ b/libusb/io.c
@@ -1948,8 +1948,8 @@ static int get_next_timeout(libusb_context *ctx, struct timeval *tv,
* non-blocking mode
* \returns 0 on success, or a LIBUSB_ERROR code on failure
*/
-int API_EXPORTED libusb_handle_events_timeout(libusb_context *ctx,
- struct timeval *tv)
+int API_EXPORTED libusb_handle_events_timeout_check(libusb_context *ctx,
+ struct timeval *tv, int *completed)
{
int r;
struct timeval poll_timeout;
@@ -1963,8 +1963,12 @@ int API_EXPORTED libusb_handle_events_timeout(libusb_context *ctx,
retry:
if (libusb_try_lock_events(ctx) == 0) {
- /* we obtained the event lock: do our own event handling */
- r = handle_events(ctx, &poll_timeout);
+ r = 0;
+ if (completed == NULL || !*completed) {
+ /* we obtained the event lock: do our own event handling */
+ usbi_dbg("doing our own event handling");
+ r = handle_events(ctx, &poll_timeout);
+ }
libusb_unlock_events(ctx);
return r;
}
@@ -1973,16 +1977,18 @@ retry:
* notify event completion. */
libusb_lock_event_waiters(ctx);
- if (!libusb_event_handler_active(ctx)) {
- /* we hit a race: whoever was event handling earlier finished in the
- * time it took us to reach this point. try the cycle again. */
- libusb_unlock_event_waiters(ctx);
- usbi_dbg("event handler was active but went away, retrying");
- goto retry;
+ if (completed == NULL || !*completed) {
+ if (!libusb_event_handler_active(ctx)) {
+ /* we hit a race: whoever was event handling earlier finished in the
+ * time it took us to reach this point. try the cycle again. */
+ libusb_unlock_event_waiters(ctx);
+ usbi_dbg("event handler was active but went away, retrying");
+ goto retry;
+ }
+
+ usbi_dbg("another thread is doing event handling, wait for notification");
+ r = libusb_wait_for_event(ctx, &poll_timeout);
}
-
- usbi_dbg("another thread is doing event handling");
- r = libusb_wait_for_event(ctx, &poll_timeout);
libusb_unlock_event_waiters(ctx);
if (r < 0)
@@ -1993,6 +1999,21 @@ retry:
return 0;
}
+API_EXPORTED int libusb_handle_events_timeout(libusb_context *ctx,
+ struct timeval *tv)
+{
+ return libusb_handle_events_timeout_check(ctx, tv, NULL);
+}
+
+API_EXPORTED int libusb_handle_events_check(libusb_context *ctx,
+ int *completed)
+{
+ struct timeval tv;
+ tv.tv_sec = 60;
+ tv.tv_usec = 0;
+ return libusb_handle_events_timeout_check(ctx, &tv, completed);
+}
+
/** \ingroup poll
* Handle any pending events in blocking mode. There is currently a timeout
* hardcoded at 60 seconds but we plan to make it unlimited in future. For
@@ -2007,7 +2028,7 @@ int API_EXPORTED libusb_handle_events(libusb_context *ctx)
struct timeval tv;
tv.tv_sec = 60;
tv.tv_usec = 0;
- return libusb_handle_events_timeout(ctx, &tv);
+ return libusb_handle_events_timeout_check(ctx, &tv, NULL);
}
/** \ingroup poll
diff --git a/libusb/libusb.h b/libusb/libusb.h
index e2cc606..d46c68b 100644
--- a/libusb/libusb.h
+++ b/libusb/libusb.h
@@ -1275,7 +1275,10 @@ int LIBUSB_CALL libusb_wait_for_event(libusb_context *ctx, struct timeval *tv);
int LIBUSB_CALL libusb_handle_events_timeout(libusb_context *ctx,
struct timeval *tv);
+int LIBUSB_CALL libusb_handle_events_timeout_check(libusb_context *ctx,
+ struct timeval *tv, int *completed);
int LIBUSB_CALL libusb_handle_events(libusb_context *ctx);
+int LIBUSB_CALL libusb_handle_events_check(libusb_context *ctx, int *completed);
int LIBUSB_CALL libusb_handle_events_locked(libusb_context *ctx,
struct timeval *tv);
int LIBUSB_CALL libusb_pollfds_handle_timeouts(libusb_context *ctx);
diff --git a/libusb/sync.c b/libusb/sync.c
index f6dc2a7..d040d98 100644
--- a/libusb/sync.c
+++ b/libusb/sync.c
@@ -102,13 +102,13 @@ int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle,
}
while (!completed) {
- r = libusb_handle_events(HANDLE_CTX(dev_handle));
+ r = libusb_handle_events_check(HANDLE_CTX(dev_handle), &completed);
if (r < 0) {
if (r == LIBUSB_ERROR_INTERRUPTED)
continue;
libusb_cancel_transfer(transfer);
while (!completed)
- if (libusb_handle_events(HANDLE_CTX(dev_handle)) < 0)
+ if (libusb_handle_events_check(HANDLE_CTX(dev_handle), &completed) < 0)
break;
libusb_free_transfer(transfer);
return r;
@@ -172,13 +172,13 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle,
}
while (!completed) {
- r = libusb_handle_events(HANDLE_CTX(dev_handle));
+ r = libusb_handle_events_check(HANDLE_CTX(dev_handle), &completed);
if (r < 0) {
if (r == LIBUSB_ERROR_INTERRUPTED)
continue;
libusb_cancel_transfer(transfer);
while (!completed)
- if (libusb_handle_events(HANDLE_CTX(dev_handle)) < 0)
+ if (libusb_handle_events_check(HANDLE_CTX(dev_handle), &completed) < 0)
break;
libusb_free_transfer(transfer);
return r;