From 41345b0527c69b8de85e4afafb92ec5f7a9b03ff Mon Sep 17 00:00:00 2001 From: Alex P Date: Sat, 8 Nov 2025 00:43:09 +0200 Subject: [PATCH] refactor: use atomic.Pointer for thread-safe inputSource access - Replace mutex-protected inputSource with atomic.Pointer for lock-free reads - Eliminate ~100 mutex operations per second in audio packet hot path - Add defer pattern for safer mutex unlock in enable/disable functions - Add error logging for audio write failures - Consolidate Makefile lint commands for brevity --- Makefile | 6 ++---- audio.go | 35 ++++++++++++++++++----------------- main.go | 2 +- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index fc1acb7b..8c2b18ef 100644 --- a/Makefile +++ b/Makefile @@ -205,14 +205,12 @@ lint-go-fix: build_audio_deps # Run UI linting locally (mirrors GitHub workflow ui-lint.yml) lint-ui: @echo "Running UI lint..." - @cd ui && npm ci - @cd ui && npm run lint + @cd ui && npm ci && npm run lint # Run UI linting with auto-fix lint-ui-fix: @echo "Running UI lint with auto-fix..." - @cd ui && npm ci - @cd ui && npm run lint:fix + @cd ui && npm ci && npm run lint:fix # Legacy alias for UI linting (for backward compatibility) ui-lint: lint-ui diff --git a/audio.go b/audio.go index 374a98b7..348dda04 100644 --- a/audio.go +++ b/audio.go @@ -14,7 +14,7 @@ import ( var ( audioMutex sync.Mutex outputSource audio.AudioSource - inputSource audio.AudioSource + inputSource atomic.Pointer[audio.AudioSource] outputRelay *audio.OutputRelay inputRelay *audio.InputRelay audioInitialized bool @@ -63,13 +63,15 @@ func startAudio() error { // Start input audio if not running, USB audio enabled, and input enabled ensureConfigLoaded() - if inputSource == nil && audioInputEnabled.Load() && config.UsbDevices != nil && config.UsbDevices.Audio { + if inputSource.Load() == nil && audioInputEnabled.Load() && config.UsbDevices != nil && config.UsbDevices.Audio { alsaPlaybackDevice := "hw:1,0" // USB speakers // Create CGO audio source - inputSource = audio.NewCgoInputSource(alsaPlaybackDevice) + newInputSource := audio.NewCgoInputSource(alsaPlaybackDevice) + var audioSrc audio.AudioSource = newInputSource + inputSource.Store(&audioSrc) - inputRelay = audio.NewInputRelay(inputSource) + inputRelay = audio.NewInputRelay(newInputSource) if err := inputRelay.Start(); err != nil { audioLogger.Error().Err(err).Msg("Failed to start input relay") } @@ -96,9 +98,9 @@ func stopInputLocked() { inputRelay.Stop() inputRelay = nil } - if inputSource != nil { - inputSource.Disconnect() - inputSource = nil + if source := inputSource.Load(); source != nil { + (*source).Disconnect() + inputSource.Store(nil) } } @@ -169,8 +171,8 @@ func SetAudioOutputEnabled(enabled bool) error { } } else { audioMutex.Lock() + defer audioMutex.Unlock() stopOutputLocked() - audioMutex.Unlock() } return nil @@ -188,8 +190,8 @@ func SetAudioInputEnabled(enabled bool) error { } } else { audioMutex.Lock() + defer audioMutex.Unlock() stopInputLocked() - audioMutex.Unlock() } return nil @@ -238,23 +240,22 @@ func handleInputTrackForSession(track *webrtc.TrackRemote) { continue // Drop frame but keep reading } - // Get source in single mutex operation (hot path optimization) - audioMutex.Lock() - source := inputSource - audioMutex.Unlock() + // Get source atomically (hot path optimization) + source := inputSource.Load() if source == nil { continue // No relay, drop frame but keep reading } - if !source.IsConnected() { - if err := source.Connect(); err != nil { + if !(*source).IsConnected() { + if err := (*source).Connect(); err != nil { continue } } - if err := source.WriteMessage(0, opusData); err != nil { - source.Disconnect() + if err := (*source).WriteMessage(0, opusData); err != nil { + (*source).Disconnect() + audioLogger.Warn().Err(err).Str("track_id", myTrackID).Msg("failed to write audio message") } } } diff --git a/main.go b/main.go index 4df1a746..5ccd78d9 100644 --- a/main.go +++ b/main.go @@ -133,7 +133,7 @@ func Main() { signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) <-sigs - logger.Info().Msg("JetKVM Shutting Down") + logger.Log().Msg("JetKVM Shutting Down") stopAudio()