Different behavior of nonblocking hid_read on different OSs? #33

Open
opened 2019-06-24 20:58:10 -05:00 by Be-ing · 6 comments
Be-ing commented 2019-06-24 20:58:10 -05:00 (Migrated from github.com)

The Linux libusb and macOS backends maintain an internal buffer of 30 (where did this number come from?) input reports. If I understand correctly, when hid_read is called repeatedly, one input report is returned at a time in the order they were received. If that is correct, then I think the correct way to use nonblocking calls to hid_read on a polling timer would be to loop until hid_read returns <= 0. However, the Windows implementation does not seem to maintain such an internal buffer, so I'm not sure how many times to call hid_read on each polling iteration. I have searched for examples of how to use hid_read in the nonblocking mode but have not found any useful information about this.

For background context, in Mixxx, our current use of HIDAPI has separate threads for reading and writing with the reading thread using hid_read_timeout with a 500ms timeout. However, a Windows user found that when writing data frequently, Mixxx would stop reading incoming data. We presume this is because of the read and write thread trying to access the device concurrently, so we are trying to move both reading and writing into the same thread and use polling, nonblocking reading. I'm not entirely certain that was the issue, so switching to using polling might be moot.

https://github.com/mixxxdj/mixxx/pull/2179
https://mixxx.zulipchat.com/#narrow/stream/113295-controller-mapping/topic/Is.20shutdown.20called.20when.20reloading.20script.3F

The Linux libusb and macOS backends maintain an internal buffer of 30 (where did this number come from?) input reports. If I understand correctly, when hid_read is called repeatedly, one input report is returned at a time in the order they were received. If that is correct, then I think the correct way to use nonblocking calls to hid_read on a polling timer would be to loop until hid_read returns <= 0. However, the Windows implementation does not seem to maintain such an internal buffer, so I'm not sure how many times to call hid_read on each polling iteration. I have searched for examples of how to use hid_read in the nonblocking mode but have not found any useful information about this. For background context, in Mixxx, our current use of HIDAPI has separate threads for reading and writing with the reading thread using hid_read_timeout with a 500ms timeout. However, a Windows user found that when writing data frequently, Mixxx would stop reading incoming data. We presume this is because of the read and write thread trying to access the device concurrently, so we are trying to move both reading and writing into the same thread and use polling, nonblocking reading. I'm not entirely certain that was the issue, so switching to using polling might be moot. https://github.com/mixxxdj/mixxx/pull/2179 https://mixxx.zulipchat.com/#narrow/stream/113295-controller-mapping/topic/Is.20shutdown.20called.20when.20reloading.20script.3F
Youw commented 2019-06-25 07:22:36 -05:00 (Migrated from github.com)

our current use of HIDAPI has separate threads for reading and writing with the reading thread using hid_read_timeout with a 500ms timeout

I believe that's the only way HIDAPI gives you to use it in a separate thread


and yes, Windows implementation is a bit different, than others, and doesn't seems to be thread safe
check this discussion: https://github.com/signal11/hidapi/issues/45

> our current use of HIDAPI has separate threads for reading and writing with the reading thread using hid_read_timeout with a 500ms timeout I believe that's the only way HIDAPI gives you to use it in a separate thread --- and yes, Windows implementation is a bit different, than others, and doesn't seems to be thread safe check this discussion: https://github.com/signal11/hidapi/issues/45
Be-ing commented 2019-06-25 15:31:19 -05:00 (Migrated from github.com)

I'm not sure the scenarios described in that old discussion are the same as what we have. We have reading in one thread and writing in another. If that's not safe, what should we do? Guard each read and write with a mutex? Or move them to one thread and run that thread on a polling timer with nonblocking reads? If we use nonblocking reads, how many calls to hid_read do we need to do on each poll?

I'm not sure the scenarios described in that old discussion are the same as what we have. We have reading in one thread and writing in another. If that's not safe, what should we do? Guard each read and write with a mutex? Or move them to one thread and run that thread on a polling timer with nonblocking reads? If we use nonblocking reads, how many calls to hid_read do we need to do on each poll?
Youw commented 2019-06-25 16:32:30 -05:00 (Migrated from github.com)

there shouldn't be such thing as "how many" - normally you poll intinitelly, until hid_read_timeout return value <0 (an error), or you stop your polling externally

I never needed/tried using both hid_read and hid_write in separate threads, so I wouldn't have a solution for you

what I'd suggest, is you to try to debug hid.c on Windows and see is there an issue that can be fixed (a synchronization, or smth) and develop/submit patch

PR(s) are welcome

there shouldn't be such thing as "how many" - normally you poll intinitelly, until `hid_read_timeout` return value <0 (an error), or you stop your polling externally I never needed/tried using both `hid_read` and `hid_write` in separate threads, so I wouldn't have a solution for you what I'd suggest, is you to try to debug `hid.c` on Windows and see is there an issue that can be fixed (a synchronization, or smth) and develop/submit patch PR(s) are welcome
Be-ing commented 2019-06-25 23:27:26 -05:00 (Migrated from github.com)

there shouldn't be such thing as "how many" - normally you poll intinitelly, until hid_read_timeout return value <0 (an error), or you stop your polling externally

But between each iteration of the poll there will likely be many incoming packets. hid_read only returns one packet at a time. If we don't process all the intermediate packets between each poll, we could miss a button press for example. Also if we only called hid_read once each poll, IIUC that would break devices that use multiple different input packets.

> there shouldn't be such thing as "how many" - normally you poll intinitelly, until hid_read_timeout return value <0 (an error), or you stop your polling externally But between each iteration of the poll there will likely be many incoming packets. hid_read only returns one packet at a time. If we don't process all the intermediate packets between each poll, we could miss a button press for example. Also if we only called hid_read once each poll, IIUC that would break devices that use multiple different input packets.
Youw commented 2019-06-26 05:41:20 -05:00 (Migrated from github.com)

do you have delays between pooling?

do you have delays between pooling?
mcuee commented 2021-06-14 02:35:38 -05:00 (Migrated from github.com)
About thread safety on Linux and macOS https://github.com/signal11/hidapi/issues/45 https://github.com/signal11/hidapi/issues/95
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-libusb/hidapi#33
No description provided.