Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aborting async. transfers and safely closing devices is too complex #700

Closed
ghost opened this issue Feb 26, 2020 · 1 comment
Closed

Aborting async. transfers and safely closing devices is too complex #700

ghost opened this issue Feb 26, 2020 · 1 comment
Labels

Comments

@ghost
Copy link

ghost commented Feb 26, 2020

Assume you have a multi-threaded application that has a lot of in-flight async. transfers (such as continuous data transfer from a device). If you want to safely close the USB device, you need to:

  1. keep a list of all transfers
  2. call libusb_cancel_transfer() on each of it
  3. wait until their completion callbacks have been called
  4. call libusb_close

I think this is too complex. I'd really just want to be able to stop transfers and/or close a device in a non-blocking and instant way. I'd prefer something like this:

  1. being able to instanly stop libusb_transfers and make libusb completely forget about them
  2. being able to just close libusb_device_handles without having to worry about ongoing transfers
  3. maybe provide an event queue to signal libusb_transfer completion instead of a tricky callback

Now, the first suggestion would probably be to set the LIBUSB_TRANSFER_FREE_BUFFER and LIBUSB_TRANSFER_FREE_TRANSFER flags on each transfer. But you'd still need to clear the completion callbacks (see below), and it doesn't seem to solve this problem anyway (other than fixing memory leaks).

libusb_cancel_transfer() is not enough either. It will still call the completion callback. The function doesn't even tell you whether a callback will be called. (It can return LIBUSB_ERROR_NOT_FOUND when the transfer was already canceled, and the completion callback is still to be called.)

Can you safely set libusb_transfer.callback to NULL? The user has no control over when libusb reads this field. Do you need to lock via libusb_lock_events()? Do you need to do it on whichever thread you call libusb_handle_events()? Or is it not possible? In general, I'm rather confused about in which context completion callbacks can be called.

And with all that, you still need to know when you can call libusb_close(). If you just "orphan" all transfers (like described above, and for the sake of your application logic), you don't know when the transfers actually complete, so you can close the device.

Can you just call libusb_close() with transfers still ongoing? It seems it somehow synchronously cancels transfers, prints an ugly warning, and calls the completion callbacks synchronously. But judging from past issues, this is more like a hack to make API user errors less fatal.

Note that this doesn't just affect closing devices. If it's some sort of multi-function device, the application may have multiple subsystems (using different USB endpoints) that you may want to start/stop independently without closing the device. So if you want to stop transfers instantly for simplicity, you're still... troubled.

It's also possible that I'm overthinking this, and there's a way to handle this correctly. I've spent a lot time thinking about this though, and I've wrote my own 500 line helper which "garbage collects" libusb_transfers (and optionally libusb_device_handles) once nothing needs/wants them anymore. Judging by how many vaguely similar issues there are on this repo (where similar things were discussed), I assume this is a common problem though.

@ghost
Copy link
Author

ghost commented Mar 5, 2020

I'm replacing this issue with the 2 linked above, trying to be more concise and less confused.

@ghost ghost closed this as completed Mar 5, 2020
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant