From 6a7f9e99965bd0fbc982856d24c4aa5a591f797d Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 18 Nov 2025 01:15:51 +0200 Subject: [PATCH] Fix race condition in setAudioTrack by using single critical section Reduced mutex locking from 3 separate lock/unlock cycles to 1, eliminating race window and improving performance. New relay is prepared within mutex, then old resources are stopped and new relay started outside mutex to avoid blocking during CGO calls. --- audio.go | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/audio.go b/audio.go index 3306ed7d..73f76ce9 100644 --- a/audio.go +++ b/audio.go @@ -163,15 +163,32 @@ func setAudioTrack(audioTrack *webrtc.TrackLocalStaticSample) { setAudioTrackMutex.Lock() defer setAudioTrackMutex.Unlock() + // Capture old resources and update state in single critical section audioMutex.Lock() currentAudioTrack = audioTrack oldRelay := outputRelay oldSource := outputSource outputRelay = nil outputSource = nil + + // Prepare new relay if needed + var newRelay *audio.OutputRelay + var newSource audio.AudioSource + if currentAudioTrack != nil && audioOutputEnabled.Load() { + ensureConfigLoaded() + alsaDevice := "hw:1,0" + if config.AudioOutputSource == "hdmi" { + alsaDevice = "hw:0,0" + } + newSource = audio.NewCgoOutputSource(alsaDevice) + newSource.SetConfig(getAudioConfig()) + newRelay = audio.NewOutputRelay(newSource, currentAudioTrack) + outputSource = newSource + outputRelay = newRelay + } audioMutex.Unlock() - // Stop relay and disconnect source outside mutex to avoid blocking during CGO calls + // Stop old resources outside mutex to avoid blocking during CGO calls if oldRelay != nil { oldRelay.Stop() } @@ -179,28 +196,9 @@ func setAudioTrack(audioTrack *webrtc.TrackLocalStaticSample) { oldSource.Disconnect() } - audioMutex.Lock() - if currentAudioTrack != nil && audioOutputEnabled.Load() { - ensureConfigLoaded() - alsaDevice := "hw:1,0" - if config.AudioOutputSource == "hdmi" { - alsaDevice = "hw:0,0" - } - newSource := audio.NewCgoOutputSource(alsaDevice) - newSource.SetConfig(getAudioConfig()) - newRelay := audio.NewOutputRelay(newSource, currentAudioTrack) - outputSource = newSource - outputRelay = newRelay - } - audioMutex.Unlock() - - // Capture relay reference and start it outside mutex - audioMutex.Lock() - relayToStart := outputRelay - audioMutex.Unlock() - - if relayToStart != nil { - if err := relayToStart.Start(); err != nil { + // Start new relay outside mutex + if newRelay != nil { + if err := newRelay.Start(); err != nil { audioLogger.Error().Err(err).Msg("Failed to start output relay") } }