Summary
The hotplug callback documentation added in #790 states:
Calling hid_exit() from within the callback has undefined behavior: hid_exit() joins the hotplug thread, which would be joining itself.
Rather than rely entirely on documentation, the implementation should detect this case and fail fast (e.g. log an error via the standard error-string path and return without attempting the join) so misuse surfaces as observable errors rather than silent hangs.
Proposed fix
At the start of `hid_exit` (and/or the shared cleanup entrypoint), detect if the current thread is the hotplug thread using `pthread_equal(pthread_self(), hid_hotplug_context.thread)` (or the Windows / IOHID equivalent) and:
- Set a registered error string making the misuse diagnosable.
- Return early without attempting the join.
This is a defence-in-depth measure layered on top of the documented UB. It overlaps structurally with #794 (macOS self-join in the internal cleanup path); fixing both can share the same "is-self-thread" helper.
Scope
- All four backends: `libusb/hid.c`, `linux/hid.c`, `mac/hid.c`, `windows/hid.c`.
Related
Drafted with Claude Code.
Summary
The hotplug callback documentation added in #790 states:
Rather than rely entirely on documentation, the implementation should detect this case and fail fast (e.g. log an error via the standard error-string path and return without attempting the join) so misuse surfaces as observable errors rather than silent hangs.
Proposed fix
At the start of `hid_exit` (and/or the shared cleanup entrypoint), detect if the current thread is the hotplug thread using `pthread_equal(pthread_self(), hid_hotplug_context.thread)` (or the Windows / IOHID equivalent) and:
This is a defence-in-depth measure layered on top of the documented UB. It overlaps structurally with #794 (macOS self-join in the internal cleanup path); fixing both can share the same "is-self-thread" helper.
Scope
Related
Drafted with Claude Code.