Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hidapi/windows: thread local error messages #688

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

d3xMachina
Copy link

@d3xMachina d3xMachina commented Aug 10, 2024

I've implemented thread local error messages on Windows to fix the double free crash when you have a Read and a Write happening in 2 different threads on the same device or when there is a thread for Read/Write for each device.

Need testing with multiple devices per thread since I only have 1 device per thread in my setup.

@Youw
Copy link
Member

Youw commented Aug 18, 2024

While this is an interesting idea to solve the current issue, there is unresolved issue with it:
If you're to use the device in different threads, you'll get several thread_local variables, but hid_close will free only one of them. There is a mem-leak by design here.

@d3xMachina
Copy link
Author

Oh you're right, I'm working on a fix.

@mcuee mcuee added the Windows Related to Windows backend label Aug 18, 2024
@d3xMachina
Copy link
Author

d3xMachina commented Aug 21, 2024

I've added a global list which contains the thread local allocations. When a thread wants to allocate memory for a thread local variable, it adds it to this list. The list is cleared as necessary. Accesses to the list are protected with a critical section for thread safety.

The only scenarios this global list is accessed are :

  • the first time an error message is created in a thread (to store the head of the list of errors)
  • when a thread is terminated (it removes the data corresponding to the thread)
  • when a device is closed (it removes the data corresponding to the device for all threads)
  • when hid_exit is called (it removes all the data)

One caveat :

  • hid_init must be called beforehand, otherwise the CriticalSection won't be initialized. Not sure how to get around that. Maybe I could simply initialize it in DllMain on a ProcessAttached event ?

@d3xMachina
Copy link
Author

I've implemented my suggestion for the caveat I mentioned.

@Youw
Copy link
Member

Youw commented Aug 22, 2024

Thanks for the implementation!

I totaly get your motivation for this change, but I have in mind a few doubts about this:

  1. HIDAPI never been advertized as thread-safe;
  2. The complexity of implementation is a bit higher than I'd be comfortable to accept and support;
  3. Ultimately we have the same problem on other platforms and having it for Windows likely means porting (almost) the same imokementation for other backends.

I was even thinkg about an alternative implementation for this - instead of having a gloal list of thread-local vairables - have a list of per-device error_strings linked to a current thread-id. That should be a bit more simpler.

Another alternative is to introduce a hid_read_error API function, which would give only hid_read and hid_read_timeout errors - that implementation should be the most trivial on any platform, but breaks backward compatibility a bit...

@d3xMachina
Copy link
Author

d3xMachina commented Aug 22, 2024

HIDAPI never been advertized as thread-safe

While this is true, there is currently no way to have async read and write on a device without having to modify hidapi directly because of the current error message implementation. Right now you have to process them one by one or you risk a crash/undefined behavior. Having some thread safety guarantees is not a bad thing either. (the user still have to do their part, for example, the user must call hid_exit only when all threads are done using hidapi)

The complexity of implementation is a bit higher than I'd be comfortable to accept and support

Understandable!

Ultimately we have the same problem on other platforms and having it for Windows likely means porting (almost) the same imokementation for other backends.

Shouldn't be too hard as long as we find an equivalent to DllMain. I could put the tls code in its own files to avoid code duplication.

I was even thinkg about an alternative implementation for this - instead of having a gloal list of thread-local vairables - have a list of per-device error_strings linked to a current thread-id. That should be a bit more simpler.

I made an attempt here (not tested!) to see what it would look like. It's not much simplier imo (you have tls_get instead of tls_register mostly). And you have a potential lock contention each time error function are called (which is every times to set the error to null).

Another alternative is to introduce a hid_read_error API function, which would give only hid_read and hid_read_timeout errors - that implementation should be the most trivial on any platform, but breaks backward compatibility a bit...

Yeah, it's unfortunate.
Edit : maybe this could be implemented without breaking backward compatibility by having a function you call to enable separate error for read and write ? But that could be confusing for the user...

At the end of the day, you're the one who has the say. If you want to take a cautious approach, you could also make a separate branch (like for the connection callback) to check the stability over time and for those who need this.

@Youw Youw added enhancement New feature or request don't_merge Don't merge this PR as is labels Sep 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
don't_merge Don't merge this PR as is enhancement New feature or request Windows Related to Windows backend
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants