Skip to content

fix: close peer connections outside the engine lock (0.6.10) - #71

Merged
TheCryptoDonkey merged 2 commits into
mainfrom
fix/reconcile-deadlock
Sep 22, 2026
Merged

TheCryptoDonkey merged 2 commits into
mainfrom
fix/reconcile-deadlock

Conversation

@TheCryptoDonkey

Copy link
Copy Markdown
Member

Every "not responding" on 0.6.9 was the same deadlock. A roster change ran reconcile on the main thread, which closed the departed device's peer connection while holding the engine lock; that close waits for libwebrtc's signalling thread, and the signalling thread was delivering an ICE state callback that took the same lock. The three traces from the Pixel each show the main thread in PeerConnection.close under the lock and the signalling thread "waiting to lock, held by thread 1".

Two rules now hold: nothing that runs on a WebRTC callback takes the engine lock (LinkTable, lock-free updateConnectionState), and a link is closed only after the lock is released (reconcile, rebuild and the profile-one reopen hand the old link back). The engine's collectors run on the media dispatcher rather than the main thread. LinkTableTest pins both rules.

Second commit bumps to 0.6.10 (33) and records the release in docs/android-release.md. This build also carries the epoch admission proof from #70, which the deployed web keeper now requires.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TgQJ1LHZjQG6wiKVfZcXPs

Every "not responding" on 0.6.9 was the same deadlock. A roster change
ran reconcile on the main thread, which closed the departed device's
peer connection while holding the engine lock; that close waits for
libwebrtc's signalling thread, and the signalling thread was delivering
an ICE state callback that took the same lock. The three traces from the
Pixel each show the main thread in PeerConnection.close under the lock
and the signalling thread "waiting to lock, held by thread 1".

Two rules now hold. Nothing that runs on a WebRTC callback takes the
engine lock: link identity lives in a LinkTable readable from any thread
and updateConnectionState takes no lock. And a link is closed only after
the lock is released: reconcile, rebuild and the profile-one reopen hand
the old link back and close it outside, as closeLinks always did. The
engine's collectors also run on the media dispatcher rather than the
caller's main thread, so a slow native close can no longer stall input
even without a deadlock.

LinkTableTest pins both rules: a state read completes while another
thread holds the engine lock, and reconcile opens under the lock and
hands back what is gone for closing outside it.

Claude-Session: https://claude.ai/code/session_01TgQJ1LHZjQG6wiKVfZcXPs
@TheCryptoDonkey
TheCryptoDonkey merged commit f4a1efa into main Sep 22, 2026
10 of 12 checks passed
@TheCryptoDonkey
TheCryptoDonkey deleted the fix/reconcile-deadlock branch September 22, 2026 16:57
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.

1 participant