Skip to content

fix: move blocking IOBluetooth release calls off the main thread - #101

Merged
MegaManSec merged 1 commit into
mainfrom
fix/offmain-iobluetooth
Aug 7, 2026
Merged

fix: move blocking IOBluetooth release calls off the main thread#101
MegaManSec merged 1 commit into
mainfrom
fix/offmain-iobluetooth

Conversation

@MegaManSec

Copy link
Copy Markdown
Owner

From the adversarial review sweep (confirmed finding). unregisterFromPC/disconnectPeripheral ran remove/closeConnection — synchronous bluetoothd IPC, documented by Apple as blocking, seconds against a dead or out-of-range device — on the main thread, on every handoff. Their bodies now run on bluetoothQueue; unregisterFromPC gained an optional main-queue completion.

Ordering is preserved explicitly where it mattered:

  • Full-set and single-send handoffs paint .releasing before the releases start, and the release path leaves .releasing rows alone — so the isAnyPeripheralTransitioning re-entrancy guards see the in-flight handoff across the whole async window (a double-pressed hotkey can no longer reverse into a take mid-handoff; this closes a hole the review caught in the first draft).
  • The disconnect poll loops keep their fast first check via serial-queue FIFO; the incoming UNREGISTER acks were already deliberately best-effort and are unchanged.

Known secondary effect (review, accepted): a peer-commanded release can lag its ack while bluetoothQueue is busy, occasionally degrading a first-attempt take to a watchdog/watcher retry — final state unaffected.

Test: full-set switch both directions; double-press the toggle hotkey mid-switch (second press must no-op); single release/take from the dropdown; release an already-disconnected peripheral; a lid-close/wake cycle. Typechecked here; please build before merging.

remove/closeConnection are synchronous bluetoothd IPC — seconds against a
dead or out-of-range device — and every handoff ran them on main. Move
them to bluetoothQueue; callers that sequence on the release get explicit
completions. The full-set and single-send handoffs paint .releasing before
the releases start and the release path leaves .releasing rows alone, so
the isAnyPeripheralTransitioning re-entrancy guards see the in-flight
handoff for the whole async window; the disconnect poll loops keep their
fast first check via queue FIFO.
@MegaManSec
MegaManSec merged commit e2949de into main Aug 7, 2026
2 checks passed
@MegaManSec
MegaManSec deleted the fix/offmain-iobluetooth branch August 7, 2026 06:55
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 2.25.3 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@MegaManSec MegaManSec changed the title Move blocking IOBluetooth release calls off the main thread fix: move blocking IOBluetooth release calls off the main thread Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant