How to force a libusb event so that libusb_handle_events() returns

Viewed 2879

Suppose I have a libusb program that just uses the hotplug API. You register a callback and then apparently have to call libusb_handle_events() in a loop which then calls your hotplug callback.

int LIBUSB_CALL hotplugCallback(libusb_context* ctx,
                                libusb_device* device,
                                libusb_hotplug_event event,
                                void* user_data)
{
    cout << "Device plugged in or unplugged";
}

void main()
{
    libusb_init(nullptr);

    libusb_hotplug_register_callback(nullptr,
                                    static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
                                    LIBUSB_HOTPLUG_NO_FLAGS,
                                    LIBUSB_HOTPLUG_MATCH_ANY,
                                    LIBUSB_HOTPLUG_MATCH_ANY,
                                    LIBUSB_HOTPLUG_MATCH_ANY,
                                    &hotplugCallback,
                                    this,
                                    &hotplugCallbackHandle);

    for (;;)
    {
        if (libusb_handle_events_completed(nullptr, nullptr) != LIBUSB_SUCCESS)
            return 1;
    }

    return 0;
}

The question is, without timeout hacks how can I exit this event loop cleanly? I can't find any functions that force libusb_handle_events() (or libusb_handle_events_completed()) to return. In theory they could just never return.

3 Answers

Sorry if this is late.

The question could have been phrased better but I'm assuming (from your comment updates) that your actual program resembles something a little closer to this:

int LIBUSB_CALL hotplugCallback(libusb_context *ctx,
                                libusb_device *device,
                                libusb_hotplug_event event,
                                void *user_data) {
    cout << "Device plugged in or unplugged";
}

void SomeClass::someFunction() {
    libusb_init(nullptr);
    libusb_hotplug_register_callback(nullptr,
                                static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
                                LIBUSB_HOTPLUG_NO_FLAGS,
                                LIBUSB_HOTPLUG_MATCH_ANY,
                                LIBUSB_HOTPLUG_MATCH_ANY,
                                LIBUSB_HOTPLUG_MATCH_ANY,
                                &hotplugCallback,
                                this,
                                &hotplugCallbackHandle);

    this->thread = std::thread([this]() {
        while (this->handlingEvents) {
            int error = libusb_handle_events_completed(context, nullptr);
        }
    });
}

Let's say your object is being deallocated and, no matter what is happening on the USB bus, you don't care and you want to clean up your thread.

You negate this->handlingEvents and you call thread.join() and the thread hangs for 60 seconds and then execution resumes.

This is done because the default behavior of libusb_handle_events_completed calls libusb_handle_events_timeout_completed and passes in a 60 second timeout interval with plans to make it infinite.

The way you force libusb_handle_events_completed to return is you call libusb_hotplug_deregister_callback which wakes up libusb_handle_events(), causing the function to return.

There is more info about this behavior in the docs.

So your destructor (or wherever you want to stop listening immediately) for the class could look something like this:

SomeClass::~SomeClass() {
    this->handlingEvents = false;
    libusb_hotplug_deregister_callback(context, hotplugCallbackHandle);
    if (this->thread.joinable()) this->thread.join();

    libusb_exit(this->context);
}

There is no functions in libusb that force libusb_handle_events() to return.

It's recommended to use libusb_handle_events() in a dedicated thread so your main thread will not be blocked by this call. Even though, if you need to manipulate the call of the event handler you can put the call in a while(condition) and change the condition state in your main thread.

Libusb documentation details this here.

Related