diff options
author | Vitali Lovich <vlovich@aliph.com> | 2011-03-16 19:51:40 -0700 |
---|---|---|
committer | Peter Stuge <peter@stuge.se> | 2011-06-15 02:45:20 +0200 |
commit | a240f5c854665dffba5fcfca2739f1c14e4d355a (patch) | |
tree | cd753319c6b7a013306e2d7b9dd7f9270e149670 | |
parent | 1ac6183419412f6efe56cf24a7270eaef95bf430 (diff) | |
download | libusb-a240f5c854665dffba5fcfca2739f1c14e4d355a.tar.gz |
Clean up in-flight transfers and device handle when closing a device
Any in-flight transfers should properly invalidate their references
to device handles that are being closed. Additionally, they should be
removed from the transfer-in-flight list. This is done with the events
lock held to protect against another thread processing the same transfer.
The events lock is initialized as a recursive mutex, because the device
close code might itself be called while an event is being handled.
Fixes #82.
[stuge: Trivial rework to reduce indenting]
-rw-r--r-- | libusb/core.c | 45 | ||||
-rw-r--r-- | libusb/io.c | 2 |
2 files changed, 46 insertions, 1 deletions
diff --git a/libusb/core.c b/libusb/core.c index c40d9d5..afe9e6e 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -1011,6 +1011,51 @@ out: static void do_close(struct libusb_context *ctx, struct libusb_device_handle *dev_handle) { + struct usbi_transfer *itransfer; + struct usbi_transfer *tmp; + + libusb_lock_events(ctx); + + /* remove any transfers in flight that are for this device */ + usbi_mutex_lock(&ctx->flying_transfers_lock); + + /* safe iteration because transfers may be being deleted */ + list_for_each_entry_safe(itransfer, tmp, &ctx->flying_transfers, list, struct usbi_transfer) { + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + if (transfer->dev_handle != dev_handle) + continue; + + if (!(itransfer->flags & USBI_TRANSFER_DEVICE_DISAPPEARED)) { + usbi_err(ctx, "Device handle closed while transfer was still being processed, but the device is still connected as far as we know"); + + if (itransfer->flags & USBI_TRANSFER_CANCELLING) + usbi_warn(ctx, "A cancellation for an in-flight transfer hasn't completed but closing the device handle"); + else + usbi_err(ctx, "A cancellation hasn't even been scheduled on the transfer for which the device is closing"); + } + + /* remove from the list of in-flight transfers and make sure + * we don't accidentally use the device handle in the future + * (or that such accesses will be easily caught and identified as a crash) + */ + usbi_mutex_lock(&itransfer->lock); + list_del(&itransfer->list); + transfer->dev_handle = NULL; + usbi_mutex_unlock(&itransfer->lock); + + /* it is up to the user to free up the actual transfer struct. this is + * just making sure that we don't attempt to process the transfer after + * the device handle is invalid + */ + usbi_dbg("Removed transfer %p from the in-flight list because device handle %p closed", + transfer, dev_handle); + } + usbi_mutex_unlock(&ctx->flying_transfers_lock); + + libusb_unlock_events(ctx); + usbi_mutex_lock(&ctx->open_devs_lock); list_del(&dev_handle->list); usbi_mutex_unlock(&ctx->open_devs_lock); diff --git a/libusb/io.c b/libusb/io.c index 3ef422a..d0112a1 100644 --- a/libusb/io.c +++ b/libusb/io.c @@ -1012,7 +1012,7 @@ int usbi_io_init(struct libusb_context *ctx) usbi_mutex_init(&ctx->flying_transfers_lock, NULL); usbi_mutex_init(&ctx->pollfds_lock, NULL); usbi_mutex_init(&ctx->pollfd_modify_lock, NULL); - usbi_mutex_init(&ctx->events_lock, NULL); + usbi_mutex_init_recursive(&ctx->events_lock, NULL); usbi_mutex_init(&ctx->event_waiters_lock, NULL); usbi_cond_init(&ctx->event_waiters_cond, NULL); list_init(&ctx->flying_transfers); |