Release FFI handles on Room.Disconnect#278
Open
MaxHeimbrock wants to merge 3 commits intomainfrom
Open
Conversation
Room never disposed its RoomHandle on Disconnect(), and Participant / TrackPublication / Track never disposed theirs at all. Each handle is an independent entry in the Rust FFI handle table, so dropping one does not cascade. The handles only got freed when GC eventually finalized each SafeHandle, which in practice meant the entire Rust-side room (peer connection, signaling client, libwebrtc state) plus every wrapped participant, publication, and track lingered for an unpredictable amount of time after the user-visible session had ended. Make Room implement IDisposable. Disconnect() now sends the FFI request and then runs Cleanup(), which unsubscribes from FfiClient events, walks LocalParticipant + RemoteParticipants disposing each participant + its publications + the publications' tracks, and finally disposes RoomHandle itself. The same Cleanup() runs from the Disconnected room event so server-initiated drops behave the same as client-initiated ones. OnEventReceived guards against late events arriving after Cleanup, so the FfiClient unsubscribe race is harmless. DisposeHandles is added as an internal cascade: Track disposes its own handle, TrackPublication forwards to its Track, RemoteTrackPublication also disposes its publication handle, and Participant walks _tracks before disposing its own handle. The Meet sample's OnDestroy also calls Disconnect now so a scene change or quit while still connected releases the handles instead of leaking them until process exit. Verified with the FFI handle table diagnostic: a connect / hold / disconnect cycle now drops rooms, participants, tracks, and remote publications back to zero. Local publications and audio/video source handles still leak on this path; those are addressed separately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After the previous commit, Room cleanup walked participants and disposed their handles, including the publication handles for remote tracks. Three local-side handles still leaked on every disconnect: - LocalTrackPublication never wrapped its FFI handle. When PublishTrack succeeded, OnPublish constructed the C# publication from the proto info alone and dropped e.Publication.Handle on the floor. The Rust side kept the entry alive in the FFI handle table for the rest of the process. - RtcVideoSource and RtcAudioSource owned an FFI source handle but their Dispose(bool) implementations released the preview texture, capture buffer, and pending audio frames without ever disposing the handle. The source therefore stayed registered with Rust until the SafeHandle finalizer eventually ran. Wrap the publication handle in the PublishTrackInstruction callback and dispose it through the existing DisposeHandles cascade. Add Handle disposal to the two Rtc source Dispose(bool) overrides so the Meet sample's CleanUpAllTracks now actually frees them. With this change, disconnecting after publishing local mic + camera returns the FFI handle table to its pre-connect baseline on macOS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
097c4d7 to
f234120
Compare
4631a7f to
149e4f3
Compare
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.
Summary
Room never disposed its FFI handles on disconnect — the Rust-side room (peer connection, signaling, libwebrtc state) plus every wrapped participant, publication, track, and Rtc source lingered until C# GC eventually finalized each SafeHandle. On a connect / hold / disconnect cycle the FFI handle table grew by ~10 entries that were never freed within the session.
Changes
Roomis nowIDisposable.Disconnect()and the server-initiatedDisconnectedevent both run a single idempotentCleanup()that unsubscribes fromFfiClientevents and walksLocalParticipant+RemoteParticipants, disposing each participant + its publications + the publications' tracks before disposingRoomHandleitself.MeetManager.OnDestroyalso disconnects so scene change / quit releases the handles.LocalTrackPublicationnow wraps the FFI handle returned fromPublishTrackCallbackand disposes it through the same cascade.RtcVideoSourceandRtcAudioSourceDispose(bool)overrides now dispose theirHandle.Test plan
Before
Before connecting:
LiveKit: [ffi-handles-diag] total=0 rooms=0 participants=0 tracks=0 publications=0 video_sources=0 audio_sources=0 video_streams=0 audio_streams=0 audio_frames=0 data_buffers=0 byte_boxes=0 other=0During connection:
LiveKit: [ffi-handles-diag] total=10 rooms=1 participants=2 tracks=2 publications=2 video_sources=0 audio_sources=0 video_streams=1 audio_streams=1 audio_frames=0 data_buffers=0 byte_boxes=1 other=0After disconnecting:
LiveKit: [ffi-handles-diag] total=8 rooms=1 participants=2 tracks=0 publications=2 video_sources=0 audio_sources=0 video_streams=0 audio_streams=0 audio_frames=0 data_buffers=0 byte_boxes=1 other=2Disconnected idle at 5 minutes:
LiveKit: [ffi-handles-diag] total=1 rooms=0 participants=0 tracks=0 publications=0 video_sources=0 audio_sources=0 video_streams=0 audio_streams=0 audio_frames=0 data_buffers=0 byte_boxes=1 other=0🤖 Generated with Claude Code
Fails without Rust fix: https://github.com/livekit/client-sdk-unity/actions/runs/25560080036?pr=278