Hotplug docs: clarify thread-safety, safe-to-call list, device->next contract #790
No reviewers
Labels
No labels
3rdparty
Android
API
Author does not respond
bsd
bug
build system/CI
ci-virtual-device
CMake
Contributions Welcome
Core
dependencies
device-specific
documentation
don't_merge
duplicate
enhancement
Example
Haiku
hidraw
hotplug
info required
invalid
libusb
macOS
Misc
Not an issue
question
Release
Test
Test GUI
Windows
wontfix
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gh-libusb/hidapi!790
Loading…
Reference in a new issue
No description provided.
Delete branch "connection-doc-v2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Expands the hotplug API documentation to spell out the thread-safety contract and adjacent behaviour in line with the Multi-threading Notes wiki. Supersedes #784.
What changes
Doc-only changes to
hidapi/hidapi.h:hid_hotplug_callback_fn— explicit "Execution context", "What the callback may call", "device parameter", and "Return value" sections.hid_hotplug_register_callback— new "Thread safety" section documenting that the hotplug register/deregister pair is thread-safe (the one deliberate exception to HIDAPI's general "not thread-safe" rule) and describing the lifetime of the internal hotplug thread.hid_hotplug_deregister_callback— strengthened to document full thread-safety and the already-deregistered no-op behaviour.Since version 0.16.0annotations on all new items; explicit@briefs (Doxygen autobrief is off);HID_API_HOTPLUG_ENUMERATEgated on theDEVICE_ARRIVEDbit, handle assigned first so self-deregistration works;DEVICE_LEFTpasses a fully-populated cached copy of the device info; invocation in registration order; handles are always positive (0 is a safe sentinel); implicithid_init();hid_exit()deregisters remaining callbacks; NetBSD returns -1; deregister of an unknown handle is safe and returns -1. Each of these was verified against all four backend implementations before being documented.HID_API_HOTPLUG_ENUMERATEredefined as asynchronous (third commit, maintainer decision) — the initial pass for already-connected devices is now documented to be delivered on the same internal event context as live events, from a registration-time snapshot (each device reported exactly once — by the pass or by a live event), honouring the callback return value (non-zero stops the remainder of the pass and deregisters). The callback is thus never invoked on an application thread. All four backends currently deliver this pass synchronously on the registering thread and ignore the return value; feasibility was checked against each backend before adopting this (libusb/mac: existing injection channels; linux: pending-work check in the monitor loop; windows: needs a delivery vehicle, e.g. a thread-pool work item).Why not #784's wording
#784's patch reads as a blanket "NOT safe to call hid_open/hid_open_path or any other global/non-device functions from within this callback." As @k1-801 pointed out in the review of that PR, this is overly strict: the issue is concurrency between the hotplug callback thread and any other thread that calls init/enumerate/open/close, not the callback itself. The new text follows the wiki's "may be used under conditions Y" style and enumerates the calls that are always safe from inside the callback.
Implied behaviour changes — tracked as follow-up issues
This PR documents the target semantics. A few minor implementation changes are required to bring the code in line with the docs; they are intentionally out of scope here (per maintainer decision — connection-callback is still WIP and a temporary doc/impl gap is acceptable):
device->next == NULLinside the callback — #792HID_API_HOTPLUG_ENUMERATEhonours the callback return value — #7932a.
HID_API_HOTPLUG_ENUMERATEdelivers the initial pass asynchronously on the internal event context, from a registration-time snapshot, and before any live events for that callback (currently: synchronously on the registering thread in all four backends) — issue to be filed; supersedes the delivery half of item 8 below and naturally fixes #793 alongsidehid_hotplug_deregister_callback()on an unknown/already-deregistered handle returns 0 on Windows, while linux/libusb/mac (and now the docs) say -1 — issue to be filedhid_hotplug_register_callback()does not implicitlyhid_init()(the other backends do), andhid_exit()'s hotplug cleanup is guarded byif (hid_mgr)— so hotplug-only usage on macOS leaks the hotplug thread and callback list athid_exit()— issue to be filedhid_error(NULL)— issue to be filedAlso captured as follow-ups — existing implementation bugs that predate this doc work:
hid_internal_hotplug_cleanup— #794hid_exitfrom within a hotplug callback should fail fast rather than deadlock — #795ENUMERATEsnapshot, so devices arriving in that window are missed entirely (linux/mac subscribe first and can at worst deliver a duplicate) — resolved by design by the snapshot-replay semantics of item 2a; fold into that issueNot changing
Drafted with Claude Code.