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

USBTransfer finalize issue #102

Open
WeiminWangKolmostar opened this issue Aug 2, 2024 · 3 comments
Open

USBTransfer finalize issue #102

WeiminWangKolmostar opened this issue Aug 2, 2024 · 3 comments

Comments

@WeiminWangKolmostar
Copy link

import usb1

VENDOR_ID = 0x04b4
PRODUCT_ID = 0x0008

STREAM_IO_INTERFACE = 3
STREAM_IN_ENDPOINT = 0x85

with usb1.USBContext() as context:
    handle = context.openByVendorIDAndProductID(
        VENDOR_ID,
        PRODUCT_ID,
        skip_on_error=True,
    )
    if handle is None:
        # Device not present, or user is not allowed to access device.
        raise RuntimeError("Could not open device.")

    with handle.claimInterface(STREAM_IO_INTERFACE):
        # Build a list of transfer objects and submit them to prime the pump.
        transfer_list = []
        for _ in range(4):
            transfer = handle.getTransfer()
            transfer.setBulk(
                usb1.ENDPOINT_IN | STREAM_IN_ENDPOINT,
                1024,
                callback=print,
            )
            transfer.submit()
            transfer_list.append(transfer)

        # handle.getTransfer()  # uncomment this line will cause segment fault
        for t in transfer_list:
            t.cancel()

In above code, if I don't cancel the transfers, the program will hang on exit.
By using Ctrl-C, I can see the program blocks on https://github.com/vpelletier/python-libusb1/blob/master/usb1/__init__.py#L1083 , and then it blocks on https://github.com/vpelletier/python-libusb1/blob/master/usb1/__init__.py#L307

Another issue is that if transfer.setBulk() is never invoked after create the transfer, segment fault will occur.

@WeiminWangKolmostar
Copy link
Author

The bug to cause the hang on issue:

If user didn't cancel the transfer, during the USBDeviceHandle finalizing, the transfer.cancel() actually not work since the interface is released (USBErrorNoDevice will be raised when try to cancel transfer). So the while inflight loop will never exit.

cancelled_set = set_()
while inflight:
for transfer in tuple(inflight):
if transfer not in cancelled_set:
try:
transfer.cancel()
except (USBErrorNotFound_, USBErrorNoDevice_):
pass
cancelled_set.add(transfer)
try:
context.handleEvents()
except USBErrorInterrupted_:
pass

@WeiminWangKolmostar
Copy link
Author

As for the segment fault error, seems a libusb bug in macOS. See libusb/libusb#1059

@vpelletier
Copy link
Owner

Thank you for the very detailed bug report.

If user didn't cancel the transfer, during the USBDeviceHandle finalizing, the transfer.cancel() actually not work since the interface is released (USBErrorNoDevice will be raised when try to cancel transfer). So the while inflight loop will never exit.

This code makes the assumption that every submitted transfer (libusb_submit_transger called and returned 0) will eventually have its callback executed, whatever the outcome of the transfer:

  • completed successfully
  • cancelled (while cancellation is meaningful, so in your example it is before releasing the interface)
  • completed with an error (device unplugged, ...)

In the case you describe, this seems to not happen when you release the interface. I think in this case the kernel is the piece of code responsible for completing all in-flight transfers with an error in such case, and that libusb would then "just work": it would get the transfer completion from the kernel, call the transfer callback, which would remove it from the inflight set.

...unless libusb actively does something to prevent that from happening when releasing the interface.

Could you try increasing libusb verbosity to try to see what is happening at its level ? See USBContext.setDebug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants