Skip to content

fix(win): stop Disconnect from freezing the UI, and a 100% CPU spin on link loss - #21

Merged
AmitRajput-Dev merged 1 commit into
AmitRajput-Dev:mainfrom
ssuyash3000:fix/windows-disconnect-hang
Oct 28, 2026
Merged

AmitRajput-Dev merged 1 commit into
AmitRajput-Dev:mainfrom
ssuyash3000:fix/windows-disconnect-hang

Conversation

@ssuyash3000

Copy link
Copy Markdown
Contributor

Three related problems around teardown on Windows:

  1. BluetoothWrapper::disconnect() took _connectorMtx before closing the
    socket, but a worker thread holds that same lock while blocked in recv().
    Closing the socket is what releases that thread, so the UI thread waited on
    the lock — Disconnect froze the window until the 2.5s recv timeout expired,
    and closing the app while a read was in flight hung it. On Windows, close
    first, then take the lock; other platforms keep the old order.

  2. _socket was written by the UI thread (disconnect) while a worker thread
    read it inside send()/recv() — a data race that could hand recv() a
    closed or recycled handle. It is now std::atomic<SOCKET>, swapped through
    a single _closeSocket().

  3. recv() returning 0 (graceful close by the headphones) was treated as a
    successful read of an empty buffer, so BluetoothWrapper::_readMessage()
    looped forever looking for a start marker — one core pegged until the app
    was killed. Both the connector and the wrapper now treat it as a
    disconnect.

Also loops send() until the whole frame is out: a partial send left the
device waiting for the rest of a command that never arrived.

…n link loss

Three related problems around teardown on Windows:

1. `BluetoothWrapper::disconnect()` took `_connectorMtx` before closing the
   socket, but a worker thread holds that same lock while blocked in `recv()`.
   Closing the socket is what releases that thread, so the UI thread waited on
   the lock — Disconnect froze the window until the 2.5s recv timeout expired,
   and closing the app while a read was in flight hung it. On Windows, close
   first, then take the lock; other platforms keep the old order.

2. `_socket` was written by the UI thread (disconnect) while a worker thread
   read it inside `send()`/`recv()` — a data race that could hand `recv()` a
   closed or recycled handle. It is now `std::atomic<SOCKET>`, swapped through
   a single `_closeSocket()`.

3. `recv()` returning 0 (graceful close by the headphones) was treated as a
   successful read of an empty buffer, so `BluetoothWrapper::_readMessage()`
   looped forever looking for a start marker — one core pegged until the app
   was killed. Both the connector and the wrapper now treat it as a
   disconnect.

Also loops `send()` until the whole frame is out: a partial send left the
device waiting for the rest of a command that never arrived.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@AmitRajput-Dev

Copy link
Copy Markdown
Owner

Merged via #30, together with the rest of your Windows/Linux fixes (#18–#22). Thank you, clear write-ups and small focused diffs made these easy to review. CI is green on Windows and Ubuntu, and they'll be in the next Windows build.

fredybp pushed a commit to fredybp/SonyBridge that referenced this pull request Nov 4, 2026
… fuzzing/hardening

Conflicts with AmitRajput-Dev#28 (multipoint) resolved:
- Multipoint "Connect to 2 devices": keep AmitRajput-Dev#28's name-based general-settings
  slot lookup (MULTIPOINT_SETTING in D1..D4) and drop AmitRajput-Dev#7's hard-coded 0xd1
  touch-sensor probe; AmitRajput-Dev#7's GadgetBridge write (d8 <slot> 00 <v> + 98 00 06 01)
  now targets the discovered slot. One set of bridge/model properties.
- Bridge: keep AmitRajput-Dev#7's shared_ptr + single serial I/O queue; multipoint runs on
  it too (its own queue bought nothing behind the connector mutex). Slot
  search moved after the capability probes so battery/EQ show immediately.
- initDevice: v1/v2 init from AmitRajput-Dev#7; table-2 flag only on v2.
- Reset _initialized on every new session and release the dead session when
  the headset drops the link, so a reconnect re-runs the handshake.
- Switch/fix decisions read playback/pin state inside the queued write.
- Devices card shown only when the headset returned a list or pin state.
- BluetoothWrapper: one zero-byte recv guard (AmitRajput-Dev#21 and AmitRajput-Dev#7 both added one).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants