Fix HID read busy-spin: pass 250ms, not 250ns, to ReadWithTimeout
go-hid's ReadWithTimeout takes a time.Duration, so the bare 250 was
250ns, which truncates to a 0 ms hid_read_timeout. On both Linux
(poll) and macOS (cond_timedwait) 0 ms means non-blocking, so the
button loop spun continuously: ~1.2 cores and ~7.5k context
switches/sec at idle, from startup.
Pass 250*time.Millisecond and match hid.ErrTimeout with errors.Is
ahead of the string fallback. Idle CPU drops to ~0%.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016yH8jHn1SNzNnL58CRwTi9
(cherry picked from commit 69d2926a1a)
This commit is contained in:
@@ -3,6 +3,7 @@ package device
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/jpeg"
|
"image/jpeg"
|
||||||
@@ -10,6 +11,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sstallion/go-hid"
|
"github.com/sstallion/go-hid"
|
||||||
"golang.org/x/image/draw"
|
"golang.org/x/image/draw"
|
||||||
@@ -194,8 +196,13 @@ func (sd *StreamDeck) ClearKey(keyIndex int) error {
|
|||||||
// Returns (nil, nil) on timeout — callers should check context and retry.
|
// Returns (nil, nil) on timeout — callers should check context and retry.
|
||||||
func (sd *StreamDeck) ReadButtons() ([]bool, error) {
|
func (sd *StreamDeck) ReadButtons() ([]bool, error) {
|
||||||
data := make([]byte, readReportSize)
|
data := make([]byte, readReportSize)
|
||||||
n, err := sd.dev.ReadWithTimeout(data, 250)
|
// ReadWithTimeout takes a time.Duration: a bare 250 would be 250ns, which
|
||||||
|
// truncates to a 0 ms (non-blocking) hid_read_timeout and busy-spins the loop.
|
||||||
|
n, err := sd.dev.ReadWithTimeout(data, 250*time.Millisecond)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, hid.ErrTimeout) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
// Linux hidraw returns errors (not (0,nil)) for non-fatal conditions:
|
// Linux hidraw returns errors (not (0,nil)) for non-fatal conditions:
|
||||||
// timeout waiting for data, or EINTR (signal interrupted).
|
// timeout waiting for data, or EINTR (signal interrupted).
|
||||||
// macOS IOHIDManager usually returns (0, nil) on timeout, but may also
|
// macOS IOHIDManager usually returns (0, nil) on timeout, but may also
|
||||||
|
|||||||
@@ -96,3 +96,4 @@ The biggest architectural difference is privileged commands:
|
|||||||
| Log location | `journalctl --user -u streamdeck-go` | `~/Library/Logs/streamdeck-go.log` |
|
| Log location | `journalctl --user -u streamdeck-go` | `~/Library/Logs/streamdeck-go.log` |
|
||||||
| Config path | `~/.config/streamdeck-go/` (XDG) | `~/.config/streamdeck-go/` (XDG — works fine on macOS for CLI tools) |
|
| Config path | `~/.config/streamdeck-go/` (XDG) | `~/.config/streamdeck-go/` (XDG — works fine on macOS for CLI tools) |
|
||||||
| Sleep/wake | Handled by reconnect loop | Handled by reconnect loop (same code) |
|
| Sleep/wake | Handled by reconnect loop | Handled by reconnect loop (same code) |
|
||||||
|
| HID read timeout | `hid_read_timeout` → `poll()`; 0 ms = non-blocking | `hid_read_timeout` → `pthread_cond_timedwait`; 0 ms = non-blocking. Same fix applies: pass `250*time.Millisecond` |
|
||||||
|
|||||||
Reference in New Issue
Block a user