I'm new to libusb, so I don't really understand it very well. I'm trying to do some USB communication.
I'm using the hotplug feature which works quite well. So I thought when I detect a device arrived event, I would do all communication with USB on another thread using the C++11 asynchronous feature, so I could do synchronous I/O with multiple devices to simply codes. Their asynchronous I/O is a bit confusing to me. Hopefully I can use the synchronous I/O with C++ asynchronous feature.
But I'm having a problem where some libusb call seem to hang when the code is running in C++11 asynchronous feature. When it is not running in C++11 asynchronous feature it works without any problem.
So I'm assuming it is my C++11 asynchronous feature code that's the problem.
Here is my hotplug callback:
int LIBUSB_CALL hotplug_callback(libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data)
{
std::future<void> result(std::async([] (libusb_device *d) {
libusb_device_handle *h;
printf("%s\n", "Opening");
int rc = libusb_open(d, &h);
if(rc != LIBUSB_SUCCESS) {
printf("ERROR: %s\n", libusb_error_name(rc));
return;
}
printf("%s\n", "Opened");
printf("%s\n", "Closing");
libusb_close(h);
printf("%s\n", "Closed!");
}, dev));
result.get();
return 0;
}
So with this code it hangs on libusb_close
It outputs:
Opening
Opened
Closing
The main code looks like this:
int main(int argc, char* argv[]) {
int vendor_id = 0x1234;
int product_id = 0x4556;
libusb_hotplug_callback_handle *hp = nullptr;
libusb_context *context = nullptr;
int rc = libusb_init(&context);
if(rc < 0)
{
return rc;
}
libusb_set_debug(context, LIBUSB_LOG_LEVEL_WARNING);
rc = libusb_hotplug_register_callback(
context,
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
LIBUSB_HOTPLUG_NO_FLAGS,
vendor_id,
product_id,
LIBUSB_HOTPLUG_MATCH_ANY,
hotplug_callback,
NULL,
hp
);
if (LIBUSB_SUCCESS != rc) {
libusb_exit (context);
return rc;
}
while(1) {
rc = libusb_handle_events(context);
}
return 0;
}
Mind you this code is more prototypical, so it is not really well written. It is still in the exploration mode of libusb.
According to their own website, using multi-threading with libusb (and std::async
is essentially multi-threading if it isn’t serialised) requires quite some considerations.
For example, they specifically state:
libusb_close() will remove a file descriptor from the poll set. There are all kinds of race conditions that could arise here, so it is important that nobody is doing event handling at this time.
In your code, there is no synchronisation between the call to libusb_close
in the one thread and libusb_handle_events
in the other. Related (from the same source as above):
The issue is that if two or more threads are concurrently calling poll() or select() on libusb's file descriptors then only one of those threads will be woken up when an event arrives. The others will be completely oblivious that anything has happened.
It is entirely imaginable that what happens here is that libusb_handle_events
in the main thread is "stealing" an event that libusb_close
is waiting for, making it never return.
The bottom line is that you need to either: