Skip to content

Fix race condition in PeerConnectionTransport.trackBitrates#821

Open
adrian-niculescu wants to merge 1 commit intolivekit:mainfrom
adrian-niculescu:fix/track-bitrates-race-condition
Open

Fix race condition in PeerConnectionTransport.trackBitrates#821
adrian-niculescu wants to merge 1 commit intolivekit:mainfrom
adrian-niculescu:fix/track-bitrates-race-condition

Conversation

@adrian-niculescu
Copy link
Copy Markdown
Contributor

@adrian-niculescu adrian-niculescu commented Nov 24, 2025

Problem

Code references below are pinned to main@8f7a8a6.

PeerConnectionTransport.trackBitrates is a plain mutableMapOf<...>() (a LinkedHashMap), with no thread-safety guarantees. Reads and writes race across threads inside a single LocalParticipant.negotiate() call:

The auto-trigger is intentional and explicitly documented in LocalParticipant ("PublisherTransportObserver.onRenegotiationNeeded() gets triggered automatically so no need to call negotiate manually."). Whenever the debounce closes before the write completes, the read on the RTC thread overlaps an in-flight write — ConcurrentModificationException in ensureCodecBitrates, or a missing/inconsistent bitrate hint in the outgoing offer SDP. The race is intrinsic to this createSenderTransceiverregisterTrackBitrateInfo sequence and exists on every publish path that exercises it (SVC video with a maxBitrateBps encoding); the fast-publish coroutineScope is one such path, not the cause.

Fix

Wrap both registerTrackBitrateInfo overloads in executeRTCIfNotClosed { ... } so writes are serialized onto the same single-threaded RTC executor that already serves the read site. This matches the existing pattern used by updateRTCConfig and addIceCandidate in the same class — the RTC thread is effectively the lock for peer-connection-adjacent state.

The closed-state guard inside executeRTCIfNotClosed also gives registerTrackBitrateInfo consistent post-close semantics (silently dropped) with the surrounding APIs.

Scope

  • Independent of Lock for PeerConnectionTransport.createAndSendOffer #823 (which added a Mutex around createAndSendOffer for concurrent offer creation). That PR did not touch registerTrackBitrateInfo; this race remains on main.
  • No API or behavioral change for callers other than the write now being serialized through the RTC executor.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Nov 24, 2025

🦋 Changeset detected

Latest commit: e16141d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
client-sdk-android Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

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