Repository navigation
Conversation
The SFU opens the subscriber data channels and writes to them right away. For reliable data that arrived while the receiver was joining, it replays the cached packets the moment both channels are open. WebRTC's ObjC RTCDataChannel registers its native observer when the wrapper is created, before peerConnection(_:didOpen:) runs. Each message goes to channel.delegate, and a nil delegate drops it. Transport forwarded the open through MulticastDelegate.notify, a DispatchQueue hop, so DataChannelDrain.setChannel (and the data-track channel) set the delegate later. Messages delivered in that gap were lost with no log. Deliver the open to the delegates synchronously. Every OnMessage runs on the signaling thread after the announcement returns, so the channel now has its delegate before the first message. TransportDataChannelOpenTests calls the open callback on the RTC queue and fails unless the delegate sees the channel on that queue before the callback returns. This is the cause of the rare EncryptedDataChannelTests flake on main (decryptionFailureWithSharedKey, run 39631217851): a slow receiver join made both channels open in the same millisecond as the send. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
The open test called the WebRTC callback on the RTC queue. In production, WebRTC calls it on its signaling thread. A queue that stands in for that thread now makes the call. The recorder must see the open on that queue, not on the RTC queue, before the callback returns. A second test drives the production path. Room stages a real join, and its subscriber transport announces a reliable channel. The channel must have its DataChannelDrain delegate when the callback returns. Both tests close their transports when a step throws. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
The tests held raw data channels in their async bodies, so the last release of a channel could run on a cooperative-pool thread. That release is a blocking WebRTC call. The signaling stand-in now creates the peer connection and the channel, and parks their close and release with RTC.park. The room test resets the data channel pairs and the data track drain through their reset paths, which park the channels, before it closes the transports. A #require ran on the stand-in queue, where Swift Testing cannot tie the issue to a test. Setup failures there now throw a LiveKitError, and the awaiting test body records it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: omnigent <noreply@omnigent.ai>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The SDK could lose the first messages on a data channel that the server opens. It did not log the loss.
Cause
The SFU opens the subscriber data channels and writes to them right away. It also replays the reliable data that it held for a joining participant. WebRTC sends each message to
channel.delegate. If that delegate is nil, WebRTC drops the message.Transport.peerConnection(_:didOpen:)forwarded the open withMulticastDelegate.notify, which is an async hop. So the SDK setchannel.delegatelater, and messages that arrived in that gap were lost with no log.Fix
peerConnection(_:didOpen:)now calls the transport delegates synchronously. WebRTC runs the message callbacks on the signaling thread after the open callback returns. So the channel has its delegate before the first message.Test
TransportDataChannelOpenTestscalls the open callback from a queue that stands in for the signaling thread. It has two tests, and neither one connects to a server:Roomstages a real join. After the callback returns, the channel must have itsDataChannelDraindelegate.Follow-up
User packets can still be lost while a participant joins. If
connectionStateis not.connected,Room.engine(_:didReceiveUserPacket:)drops a user packet with no log. The SFU can send its replay before the SDK sets.connected. That fix needs a design decision, so it is not in this PR.