Potential solution to MacOS hid_init/hid_exit issue #666
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#666
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
One of the known thread-safety-related limitations on MacOS is that hid_init and hid_exit have to be called on the same thread. The only thing hid_exit really does there is destroy a HidManager. We could just leave that on the end user's shoulders, or we could resolve it internally. The problem might only arise if the user's application accidentally runs hid_init in a side thread before the main thread even gets a chance to call it (example: a static object constructor in C++ might do that).
We can't, of course, track the thread it was created in to ensure it closes in it - but, theoretically, this issue could be easily mitigated by giving the manager it's own thread to live in, which apparently would always be suspended by the sleeping RunLoop waiting for events, sacrificing zero processing power and only a small amount of ram. The manager would be created AND destroyed within the thread's function, guaranteed to be in the same thread. hid_init would have to start the thread and then wait at a barrier for the manager to be created. Then hid_exit would essentially be built the same way as hid_close, sending an event to stop the thread. That's about it.
All true.
I was thinking about having its own thread for init/deinit on macOS.
Just never had a chance to get it done.
Ran into this recently after some new threading was added to a downstream application. We decided to wrap calls documented here https://github.com/libusb/hidapi/issues/45#issuecomment-1546803030 into a single thread, which seems to have fixed the issue. Since we're actually downstream of downstream, we submitted the patch to the library we depend upon that wraps
hidapifunctionality. I'm not sure if the patch will be accepted, but I wanted to offer gratitude for documenting this.Hello, reverting this change seems to fix the issue. Any drawbacks on doing this ?
If will break other scenarios, not similar to yours.
This has bitten me today. A problem arises when you first call
init_hid_manager()from an ephemeral thread. It will bind itself to that thread. If that thread exits, then subsequent calls will crash. See https://github.com/ruabmbua/hidapi-rs/issues/175 for the downstream bug.Applying the fix mentioned by @marciopamplona does work, as does calling
init_hid_manager()first from the main thread.The problem is that it's a global, but one that's owned by the current thread. If that thread goes away, then things explode.
What scenarios will break if it's run on the main loop rather than the current runloop?
Many that work today w/o any issues, like when hid_init/hid_exit is performed in the same, but other than the main thread (I personally have a few such projects of my own). Sometime you simply do not have access to the main thread (e.g. when run from a dynamic library).
UPD: OK, maybe not many different scenarios, but one scenario used by different projects.
Thanks. I appreciate that input.
Is there a way around it? The issue I see right now is that you need to know what thread first called into hidapi and then deallocate threadapi if that thread quits.
hid_exit()while other threads are still using it?I'm trying to come up with a way to track usage across multiple threads in the Rust library, and I'm not really coming up with anything foolproof other than documentation saying to always call into hidapi first from the main thread, which doesn't seem ideal...
Global initialisation, it is a singletone.
https://github.com/libusb/hidapi/issues/666#issuecomment-2041026698
This is undefined behavior. (Depends on a platform, but most likely it will crash at some point, maybe immediately)
See also: https://github.com/libusb/hidapi/wiki/Multi%E2%80%90threading-Notes
The "ideal workaround" (sounds funny, right?) is to create a dedicated thread, and call hid_init/hid_exit (maybe even all other "global" functions) from that thread. As I mentioned in my other comment - probably best if that thread is created by hidapi itself, but I don't find that to be trivial being done in HIDAPI source right now in C.
Besides, we have a single global state right now, and a thread per-device. I'd like that to be changed for v1, anyway.