-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Dedicated HID IO thread to prevent blocking by HIDAPIs slow processing of OutputReports #4585
Conversation
039e29b
to
387bc55
Compare
… block the controller thread for several milliseconds per OutputReport: -Ensure that the ring-buffer of InputReports is polled between hid_write of two OutputReports -Move HID IO in a dedicated thread, this allows the JavaScript controller mapping to continue, while HIDAPI waits for completion of the data transfer of the OutputReport -Skip sending of OutputReports, if the data didn't changed - as already implemented for polled InputReports -Added timing information to --controllerDebug output, that mapping developers can understand the timing behavior
387bc55
to
42d5608
Compare
Does this risk bringing back the deadlock issue solved in #2179? |
No, the reason for the deadlock was that hid_read and hid_write were used in different threads, but both used the same non-thread-safe device datastructure of hidapi. |
Please note that I am not planning to decide when the PR is ready to be merged. I am done with C++ and consider myself unable to do this. Just wanted to point out my serious concerns. |
939a02e
to
ce1fe8b
Compare
Removed unnecessary parent QObject Make the pointers to hid_device structure const, but the structure itself is not const. Added a comment to explain this. Use default constructor for m_lastSentOutputReport Deleted empty deconstructors Use atomicStoreRelaxed(m_stop instead of m_stop =
ce1fe8b
to
4ebaec9
Compare
Please separate distinct changes into separate commits whenever feasible. You don't need to go back and split up the last commit, but please keep that in mind going forward. If we want to squash them at the end, we can. |
Use standard Event Loop for HidIoThread Removed no longer needed implementation of IsPolling
3350280
to
8399b46
Compare
6fc976f
to
3cd3c3c
Compare
22007fc
to
da8b9b7
Compare
… the performance of the logging statements from milliseconds to some microseconds
8d65947
to
f2fac26
Compare
f2fac26
to
600d02a
Compare
86106e4
to
ca3e246
Compare
b42028f
to
3ba0f1c
Compare
@ywwg Please update your review after you requested changes for issues that have long been resolved. |
…tects the input report polling data Improved the comment describing this mutex
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, Jörg! Your commit messages helped a lot to keep track of the changes for the re-reviews.
I haven't tested the code in action yet, maybe later this week. But this should not block merging. It has already been confirmed that outsourcing the interaction with the HID API into a separate thread has improved the situation.
No promises that the code is correct. Nevertheless, LGTM
We should probably stash the commits before merging! |
Thank you all for your review feedback - without this, the code wouldn't have reached the maturity, that it has now! |
// This means there is always a one to one relationship to the state of control(s)/LED(s), | ||
// and if the state is not changed, there's no need to execute the time consuming hid_write again. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect and has broken the output messages driving the motor in my Kontrol S4 Mk3 mapping. The motor stops turning if no output packets are sent. To keep a constant velocity, identical output packets must be sent repeatedly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can think of three ways forward:
- Remove this optimization
- Add an optional parameter to the
send
function to opt in to allowing duplicate packets. - Add a separate function like
sendAllowDuplicates
for this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will look how to solve this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default should behavior should ignore duplicate reports. The timing and jitter of the reports is unpredictable and usually not suitable for implementing a real-time control cycle that requires exact cycle times.
I vote for adding an extra parameter to override the default behavior if needed, e.g. sendDuplicateReport
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would be fine with solution 2. too!
But I could also imagine fourth solution:
4. Add a function configureOutputReport(int ReportID, bool sendDuplicates = false) which needs to be called only once in the init section of the JS mapping code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer option 2 to add an optional parameter to the existing send
function rather than add more functions for this edge case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recommend to avoid stateful code if possible. Let the script pass an additional parameter on each invocation. It is less error prone and provides more flexibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, than I will provide a PR for 2.
This PR fixes performance issues by slow execution of the hid_write command of HIDAPI. This can block the controller mapping thread for several milliseconds (in my case of Traktor Z2 on Windows it takes ~7ms for each OutputReport). This is a performance regression introduced in #2179. The thread safe usage is guaranteed here as well, because all HIDAPI IO happens serialized in the same thread.
Improvements of this PR:
-Ensure that the ring-buffer of InputReports is polled between hid_write of two OutputReports
-Move HID IO in a dedicated thread, this allows the JavaScript controller mapping to continue, while HIDAPI waits for completion of the data transfer of the OutputReport
-Skip sending of OutputReports, if the data didn't changed - as already implemented for polled InputReports
-Skip sending superseded OutputReports, if the mapping sends them faster than HIDAPI can send them
-Added timing information to --controllerDebug output, that mapping developers can understand the timing behavior
-It does no longer relies on timers, which behave unreliable for the needed short periods and depend on the different OSes.