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.
This commit is contained in:
Alex P 2025-11-18 01:15:51 +02:00
parent d7dc8c2075
commit 6a7f9e9996
1 changed files with 21 additions and 23 deletions

View File

@ -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")
}
}