fix: break circular GObject references that leaked pipelines and sockets#465
Merged
fix: break circular GObject references that leaked pipelines and sockets#465
Conversation
Strong references to pipeline and elements captured in connect_pad_added closures in linking.rs created a circular reference: pipeline owns elements, elements own signal handler closures, closures own pipeline+elements. This prevented GStreamer from ever finalizing the pipeline, leaking all OS resources (UDP sockets, threads) on every pipeline restart. On production (br), this accumulated ~5870 leaked UDP sockets after repeated flow restarts, causing kernel socket buffer overflow and AES67 packet loss. Changes: - Replace strong refs with WeakRef in dynamic pad handler closures - Balance add_signal_watch/remove_signal_watch calls in bus watch cleanup - Add permanent runtime verification in stop_flow() that logs ERROR if pipeline or elements survive after PipelineManager drop - Add regression test that creates a real pipeline via PipelineManager and asserts full GObject finalization after stop+drop Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Never capture strong references to gst::Pipeline, gst::Element, or gst::Bin inside signal handler closures — this creates circular references that prevent pipeline finalization and leak OS resources. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ences Intentionally creates a strong pipeline ref inside a signal handler closure to verify that the weak ref detection mechanism works. If this test ever fails, it means real leaks would go undetected. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Strip info.version before comparing so patch-level version bumps without API changes don't fail the snapshot test. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CI runner lacks gstreamer1.0-plugins-good (no 'level' element). Simplify test flow to audiotestsrc → fakesink which only needs base plugins. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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
connect_pad_addedclosures inlinking.rscreated a circular reference chain: pipeline owns elements → elements own signal handler closures → closures own pipeline+elements. This prevented GStreamer from ever finalizing the pipeline, leaking all OS resources (UDP sockets, threads, file descriptors) on every pipeline restart.WeakRefin dynamic pad handler closures, balancedadd_signal_watch/remove_signal_watchref-counting in bus cleanup, and added permanent runtime verification + regression test.Changes
linking.rs— Replacepipeline.clone()andelements.clone()withWeakRefinsetup_dynamic_pad_handlers()closuresbus.rs— Balanceremove_signal_watchcalls to matchadd_signal_watchcountmod.rs— Clean up Drop impl ordering (stop QoS task before set_state)state.rs— Add runtime leak detection instop_flow(): logs ERROR if pipeline/elements survive after PipelineManager dropstate.rs— Addflow_name(),element_weak_refs()accessorsCLAUDE.md— Add rule: never capture strong GStreamer refs in signal handler closurespipeline_lifecycle_test.rs— Permanent regression test using real PipelineManagerTest plan
cargo test --test pipeline_lifecycle_testpasses (weak refs verify full finalization)🤖 Generated with Claude Code