fix(01-01): add mutex protection to Queue, Library, and Playlist SetContext methods

- Queue.SetContext acquires existing q.mu before writing q.ctx
- Library struct gets new mu sync.Mutex; SetContext and SetRescanHooks acquire it
- Playlist Service struct gets new mu sync.Mutex; SetContext and SetFavoritesConfig acquire it
- Library and Playlist release lock before calling post-init methods (registerEventHandlers, migrateExistingPlaylists)
This commit is contained in:
2026-02-28 12:09:14 -05:00
parent 4e2986e19c
commit daaa6b7f97
3 changed files with 22 additions and 0 deletions
+9
View File
@@ -76,6 +76,9 @@ type RescanHooks struct {
// Library manages scanning and querying the music collection. // Library manages scanning and querying the music collection.
type Library struct { type Library struct {
// mu protects ctx, conf, and rescanHooks from concurrent
// access during initialization.
mu sync.Mutex
ctx context.Context ctx context.Context
logger *slog.Logger logger *slog.Logger
conf *Config conf *Config
@@ -86,6 +89,9 @@ type Library struct {
// SetRescanHooks provides optional hooks for cross-cutting // SetRescanHooks provides optional hooks for cross-cutting
// orchestration during FullRescan. // orchestration during FullRescan.
func (l *Library) SetRescanHooks(h RescanHooks) { func (l *Library) SetRescanHooks(h RescanHooks) {
l.mu.Lock()
defer l.mu.Unlock()
l.rescanHooks = h l.rescanHooks = h
} }
@@ -118,7 +124,10 @@ func NewLibrary(
// SetContext sets the Wails runtime context and registers event handlers. // SetContext sets the Wails runtime context and registers event handlers.
func (l *Library) SetContext(ctx context.Context) { func (l *Library) SetContext(ctx context.Context) {
l.mu.Lock()
l.ctx = ctx l.ctx = ctx
l.mu.Unlock()
l.registerEventHandlers() l.registerEventHandlers()
} }
+10
View File
@@ -11,6 +11,7 @@ import (
"slices" "slices"
"strconv" "strconv"
"strings" "strings"
"sync"
"github.com/wailsapp/wails/v2/pkg/runtime" "github.com/wailsapp/wails/v2/pkg/runtime"
@@ -96,6 +97,9 @@ type PhantomSearchResult struct {
// Service manages playlist operations. // Service manages playlist operations.
type Service struct { type Service struct {
// mu protects ctx and favoritesConf from concurrent access
// during initialization.
mu sync.Mutex
ctx context.Context ctx context.Context
logger *slog.Logger logger *slog.Logger
db *database.DB db *database.DB
@@ -121,6 +125,9 @@ func NewService(
func (s *Service) SetFavoritesConfig( func (s *Service) SetFavoritesConfig(
provider FavoritesConfigProvider, provider FavoritesConfigProvider,
) { ) {
s.mu.Lock()
defer s.mu.Unlock()
s.favoritesConf = provider s.favoritesConf = provider
} }
@@ -128,7 +135,10 @@ func (s *Service) SetFavoritesConfig(
// one-time startup migration to bootstrap M3U8 files for // one-time startup migration to bootstrap M3U8 files for
// existing playlists. // existing playlists.
func (s *Service) SetContext(ctx context.Context) { func (s *Service) SetContext(ctx context.Context) {
s.mu.Lock()
s.ctx = ctx s.ctx = ctx
s.mu.Unlock()
s.migrateExistingPlaylists() s.migrateExistingPlaylists()
} }
+3
View File
@@ -132,6 +132,9 @@ func NewQueue(logger *slog.Logger, db *database.DB) *Queue {
// SetContext sets the Wails runtime context for event emission. // SetContext sets the Wails runtime context for event emission.
func (q *Queue) SetContext(ctx context.Context) { func (q *Queue) SetContext(ctx context.Context) {
q.mu.Lock()
defer q.mu.Unlock()
q.ctx = ctx q.ctx = ctx
} }