Skip to content

Commit 7eea384

Browse files
Youwclaude
andcommitted
Document hotplug callback thread-safety contract
Expand the doc comments on hid_hotplug_callback_fn, hid_hotplug_register_callback, and hid_hotplug_deregister_callback to make the hotplug API's thread-safety contract explicit: - The hotplug API is thread-safe; this is a documented exception to HIDAPI's general "not thread-safe" rule. - The callback runs on an internal HIDAPI thread that is distinct from the application's threads, and holds an internal mutex for the duration of each call. - Enumerate safe-to-call functions from within the callback (hid_hotplug_register/deregister_callback, hid_error(dev)) and explain why others require application-level serialisation, matching the wording style in the Multi-threading Notes wiki. - Spell out the lifetime of the device pointer, the string ownership rules, and the requirement that device->next is always NULL inside the callback. - Document hid_exit() as UB from within the callback (self-join). - Define the return-value semantics consistently for both live events and the synthetic "arrived" events delivered during HID_API_HOTPLUG_ENUMERATE. Supersedes PR #784. Behaviour changes implied by this doc (device->next=NULL, ENUMERATE honoring return value) are tracked as follow-up issues. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 1889e9f commit 7eea384

1 file changed

Lines changed: 85 additions & 19 deletions

File tree

hidapi/hidapi.h

Lines changed: 85 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -297,31 +297,77 @@ extern "C" {
297297
HID_API_HOTPLUG_ENUMERATE = (1 << 0)
298298
} hid_hotplug_flag;
299299

300-
/** @brief Hotplug callback function type. When requesting hotplug event notifications,
301-
you pass a pointer to a callback function of this type.
300+
/** @brief Hotplug callback function type.
302301
303-
This callback may be called by an internal event thread and as such it is
304-
recommended the callback do minimal processing before returning.
302+
Called by HIDAPI when a device matching the registration filter
303+
is connected or disconnected. See #hid_hotplug_register_callback.
305304
306-
hidapi will call this function later, when a matching event had happened on
307-
a matching device.
305+
## Execution context
308306
309-
Note that when callbacks are called from hid_hotplug_register_callback()
310-
because of the \ref HID_API_HOTPLUG_ENUMERATE flag, the callback return
311-
value is ignored. In other words, you cannot cause a callback to be
312-
deregistered by returning 1 when it is called from hid_hotplug_register_callback().
307+
The callback is invoked on an internal HIDAPI thread that is
308+
distinct from any thread the application creates. Every callback
309+
invocation holds an internal hotplug mutex for its duration, which
310+
means that any other thread calling hid_hotplug_register_callback()
311+
or hid_hotplug_deregister_callback() will block until the callback
312+
returns. Keep the callback short.
313+
314+
## What the callback may call
315+
316+
The hotplug API itself is thread-safe (see "Thread safety" under
317+
#hid_hotplug_register_callback) and the following calls are always
318+
safe from within the callback:
319+
320+
- hid_hotplug_register_callback()
321+
- hid_hotplug_deregister_callback() (including on its own handle)
322+
- hid_error(dev) with a non-NULL device handle
323+
324+
Any other HIDAPI function follows HIDAPI's general thread-safety
325+
rule (see the Multi-threading Notes in the project wiki): it is
326+
the application's responsibility to serialize hid_init / hid_exit /
327+
hid_enumerate / hid_open* / hid_close / hid_error(NULL) across all
328+
threads, including the hotplug callback thread. If your application
329+
already calls those functions only from one thread, calling them
330+
from the hotplug callback adds a second thread and is therefore
331+
UNSAFE unless the application adds synchronisation itself. The
332+
recommended pattern is to copy the needed fields of @p device out
333+
of the callback and handle open/close on your own thread.
334+
335+
Calling hid_exit() from within the callback has undefined behavior:
336+
hid_exit() joins the hotplug thread, which would be joining itself.
337+
338+
## device parameter
339+
340+
The @p device pointer is owned by HIDAPI and is valid only for the
341+
duration of the call. To keep any of its fields (path,
342+
serial_number, manufacturer_string, etc.) beyond the callback,
343+
copy them out; pointer fields must be deep-copied, as all strings
344+
are owned by HIDAPI.
345+
346+
The @p device->next pointer is always NULL. Each callback
347+
invocation describes exactly one device; compound or composite
348+
devices that expose multiple interfaces produce multiple callback
349+
invocations (typically delivered in quick succession).
350+
351+
## Return value
352+
353+
Return 0 to keep the callback registered. Return any non-zero
354+
value to have HIDAPI deregister the callback; no further events
355+
for this handle will be delivered, and the handle is freed as if
356+
hid_hotplug_deregister_callback() had been called. This applies
357+
to both live events and to the synthetic "arrived" events
358+
delivered during registration when #HID_API_HOTPLUG_ENUMERATE is
359+
set.
313360
314361
@ingroup API
315362
316-
@param callback_handle The hid_hotplug_callback_handle callback handle.
317-
@param device The hid_device_info of device this event occurred on event that occurred.
318-
@param event Event that occurred.
319-
@param user_data User data provided when this callback was registered.
320-
(Optionally NULL).
363+
@param callback_handle The handle of this callback.
364+
@param device The hid_device_info of the device this event occurred on.
365+
@param event Event that occurred. See \ref hid_hotplug_event.
366+
@param user_data User data provided when this callback was registered
367+
(may be NULL).
321368
322-
@returns bool
323-
Whether this callback is finished processing events.
324-
Returning non-zero value will cause this callback to be deregistered.
369+
@returns
370+
0 to stay registered; any non-zero value to be deregistered.
325371
*/
326372
typedef int (HID_API_CALL *hid_hotplug_callback_fn)(
327373
hid_hotplug_callback_handle callback_handle,
@@ -335,6 +381,20 @@ extern "C" {
335381
If @p product_id is set to 0 then any product matches.
336382
If @p vendor_id and @p product_id are both set to 0, then all HID devices will be notified.
337383
384+
## Thread safety
385+
386+
hid_hotplug_register_callback() and hid_hotplug_deregister_callback()
387+
are thread-safe. They may be called from any thread, including
388+
from within a hotplug callback. This is a deliberate exception
389+
to HIDAPI's general "not thread-safe" rule (see the
390+
Multi-threading Notes in the project wiki).
391+
392+
The first successful call to hid_hotplug_register_callback()
393+
starts an internal HIDAPI thread that runs until either (a) the
394+
last callback is deregistered, or (b) hid_exit() is called.
395+
hid_exit() must not be called from within a hotplug callback
396+
(see #hid_hotplug_callback_fn).
397+
338398
@ingroup API
339399
340400
@param vendor_id The Vendor ID (VID) of the types of device to notify about.
@@ -356,7 +416,13 @@ extern "C" {
356416

357417
/** @brief Deregister a callback from a HID hotplug.
358418
359-
This function is safe to call from within a hotplug callback.
419+
Thread-safe. May be called from any thread, including from within
420+
a hotplug callback (on its own handle or on another callback's
421+
handle). Calling it on a handle that was already deregistered,
422+
or on a handle that was never valid, is a safe no-op returning 0.
423+
424+
See "Thread safety" on #hid_hotplug_register_callback for the
425+
full thread-safety contract of the hotplug API.
360426
361427
@ingroup API
362428

0 commit comments

Comments
 (0)