vpelletier / python-libusb1

Python ctype-based wrapper around libusb1
GNU Lesser General Public License v2.1
168 stars 65 forks source link

Getting OSError: exception: access violation writing #72

Closed masoudr closed 2 years ago

masoudr commented 3 years ago

Hello, I've a created a sample class for libusb but when I call the close method I get access violation writing error. When the close method calls with destructor it works just fine. I'm not sure if I'm missing something in here.

import usb1

class USB:
    """USB class that handles IO operation with USB device
    ENDPOINT_IN: device-to-host, ENDPOINT_OUT: host-to-device"""
    # read size chunk
    READ_CHUNK = 1024

    def __init__(self, pid: hex, vid: hex, endpoint_in: hex, endpoint_out: hex) -> None:
        self.endpoint_in = endpoint_in
        self.endpoint_out = endpoint_out
        self.pid = pid
        self.vid = vid
        self.context = usb1.USBContext()

    def is_connected(self) -> bool:
        """Check if specified device is connected"""
        with usb1.USBContext() as context:
            for device in context.getDeviceIterator(skip_on_error=True):
                if device.getVendorID() == self.vid and device.getProductID() == self.pid:
                    return True

    def open(self) -> bool:
        """Open USB and initialize interface"""
        try:
            self.context.open()
            return True
        except Exception as err:
            print("open device error:", err)
            return False

    def __get_handle(self):
        """return handle object"""
        return self.context.openByVendorIDAndProductID(self.vid, self.pid)

    def close(self) -> bool:
        """Close USB"""
        try:
            self.context.close()
            return True
        except Exception as err:
            print("Close handle error:", err)
            return False

    def write(self, msg: bytearray, timeout: int = 0) -> bool:
        """write an specific msg to device"""
        handle = self.__get_handle()
        if not handle:
            return False
        try:
            with handle.claimInterface(0):
                bytes_written = handle.bulkWrite(
                    self.endpoint_out, msg, timeout)
            return bytes_written == len(msg)
        except Exception as err:
            print("write error", err)

        handle.close()
        return False

    def read(self, timeout: int = 10) -> bytearray:
        """read data from the device"""
        data = bytearray()
        handle = self.__get_handle()
        if not handle:
            return False
        try:
            with handle.claimInterface(0):
                while True:
                    try:
                        data += handle.bulkRead(self.endpoint_in,
                                                self.READ_CHUNK, timeout)
                    except usb1.USBErrorTimeout:
                        break
        except Exception as err:
            print("read error", err)
            return None

        return data

    def __del__(self):
        self.close()

if __name__ == '__main__':
    VENDOR_ID = 0x04b4
    PRODUCT_ID = 0x00f0
    ENDPOINT_IN = 0x81
    ENDPOINT_OUT = 0x01
    usb = USB(PRODUCT_ID, VENDOR_ID, ENDPOINT_IN, ENDPOINT_OUT)
    print(usb.is_connected())
    msg = 100 * bytearray(b"B")
    if usb.open():
        print("write:", usb.write(msg))
        print("read:", usb.read())
    # error after adding this line
    usb.close()

error:

Exception ignored in: <function USBDevice.__del__ at 0x000002530482A9D0>
Traceback (most recent call last):
  File "C:\Users\ABC\AppData\Roaming\Python\Python39\site-packages\usb1\__init__.py", line 1778, in __del__
    self.close()
  File "C:\Users\ABC\AppData\Roaming\Python\Python39\site-packages\usb1\__init__.py", line 1790, in close
    self.__libusb_unref_device(self.device_p)
OSError: exception: access violation writing 0x0000000000000024
mcuee commented 2 years ago

[ 0.103928] [00004f70] libusb: warning [libusb_exit] device 1.0 still referenced

This is indeed a bit surprising. In a previous try (with explicit gc.collect() calls) it did not happen, device 1.0 got somehow destroyed. No such device appear in my case (maybe running in a VM makes a difference ?). I'll try to see if I can reproduce this and identify what device this is.

If you look at your Windows 10 debug log, you will notice the following about 'some libusb_devices were leaked'. You are using libusb-1.0.24 release which gives this debug log. libusb-1.0 git now gives more detailed information about which devices were leaked.

And then there is an error in the debug log as well. error [libusb_set_pollfd_notifiers] external polling of libusb's internal event sources is not yet supported on Windows.

Closing USB
[ 0.064870] [000003b4] libusb: error [libusb_set_pollfd_notifiers] external polling of libusb's internal event sources is not yet supported on Windows
[ 0.065282] [000003b4] libusb: debug [libusb_unref_device] destroy device 1.2
[ 0.065452] [000003b4] libusb: debug [libusb_exit]
[ 0.065708] [000003b4] libusb: debug [libusb_exit] destroying default context
[ 0.065967] [000003b4] libusb: warning [libusb_exit] some libusb_devices were leaked
[ 0.066258] [000003b4] libusb: debug [usbi_remove_event_source] remove HANDLE 0000000000000238
[ 0.066573] [000003b4] libusb: debug [usbi_remove_event_source] remove HANDLE 0000000000000120
[ 0.066951] [0000117c] libusb: debug [windows_iocp_thread] I/O completion thread exiting
Close handle successfully
Calling closing method to delete self
Closing USB
Close handle successfully

I now consider my changes to be in the right direction, so I pushed them to master and dropped the wip branch. Thanks a lot for your patient testing !

Great,

mcuee commented 2 years ago

I think you are right. I tried it again (but with another Windows 10 laptop at home) and it works fine (tested with libusb-1.0.23, 1.0.24 and latest git 1.0.24.11650). So I think the fixes are good.

Going back to my original Windows 10 laptop and it also works fine, tested with latest git head now that you have integrated the patches.

(py39venv) C:\work\libusb\python_usb [master ≡ +9 ~0 -0 !]> $Env:LIBUSB_DEBUG=3
(py39venv) C:\work\libusb\python_usb [master ≡ +9 ~0 -0 !]> python .\cyusb_fx3_bulkloop_python_libusb1.py
libusb: info [winusbx_init] WinUSB DLL available (with isoch support)
libusb: error [load_usbdk_helper_dll] Failed to load UsbDkHelper.dll: [126] The specified module could not be found.
libusb: info [windows_init] UsbDk backend is not available
Number of bytes written:  100
write: True
read: bytearray(b'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB')
Closing USB
libusb: error [libusb_set_pollfd_notifiers] external polling of libusb's internal event sources is not yet supported on Windows
libusb: warning [libusb_exit] device 1.0 still referenced
Close handle successfully
Calling closing method to delete self
Closing USB
Close handle successfully
mcuee commented 2 years ago

This libusb: error [libusb_set_pollfd_notifiers] external polling of libusb's internal event sources is not yet supported on Windows probably needs to be fixed.

The libusb: warning [libusb_exit] device 1.0 still referenced warning message may or may not be a problem with python-libusb1. Reference: https://github.com/libusb/libusb/issues/974 and https://github.com/libusb/libusb/issues/988

mcuee commented 2 years ago

I will create a separate issue with the device leak issue.

vpelletier commented 2 years ago

libusb: error [libusb_set_pollfd_notifiers] external polling of libusb's internal event sources is not yet supported on Windows

Nice catch, I missed this in all the debug output.

This should be fixed by 976866d6be0ba50e12b26aab02f536fdca1d8b12 (checked with my win10 vm, the error message does not appear anymore).

mcuee commented 2 years ago

Yes the latest git fixed the warning, Thanks. I think you can closed this whole issue.

masoudr commented 2 years ago

Thanks for the contribution. I will test it with my device and will report if I got any issues.

vpelletier commented 2 years ago

I'm closing this, but please do reopen if you find that it is not fixed.