Serialize configuration updates and writes

This commit is contained in:
Brandon McGinty (chatgpt)
2026-08-10 00:18:42 -04:00
committed by Brandon McGinty
parent 874cbb7818
commit 118799d112
2 changed files with 46 additions and 2 deletions
+28 -2
View File
@@ -10,9 +10,11 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"sync"
) )
type Config struct { type Config struct {
mu sync.Mutex
config *exportableConfig config *exportableConfig
fn string fn string
} }
@@ -49,6 +51,12 @@ type eUser struct {
// SaveConfig atomically replaces the persisted configuration. Errors are // SaveConfig atomically replaces the persisted configuration. Errors are
// returned so an unavailable directory cannot crash the client. // returned so an unavailable directory cannot crash the client.
func (c *Config) SaveConfig() error { func (c *Config) SaveConfig() error {
c.mu.Lock()
defer c.mu.Unlock()
return c.saveConfigLocked()
}
func (c *Config) saveConfigLocked() error {
data, err := toml.Marshal(c.config) data, err := toml.Marshal(c.config)
if err != nil { if err != nil {
return err return err
@@ -254,18 +262,24 @@ func (c *Config) findUser(address string, username string) *eUser {
} }
func (c *Config) ToggleMute(u *gumble.User) { func (c *Config) ToggleMute(u *gumble.User) {
c.mu.Lock()
defer c.mu.Unlock()
j := c.findUser(u.GetClient().Config.Address, u.Name) j := c.findUser(u.GetClient().Config.Address, u.Name)
j.LocallyMuted = !j.LocallyMuted j.LocallyMuted = !j.LocallyMuted
u.SetLocallyMuted(j.LocallyMuted) u.SetLocallyMuted(j.LocallyMuted)
c.SaveConfig() _ = c.saveConfigLocked()
} }
func (c *Config) SetMicVolume(v float32) { func (c *Config) SetMicVolume(v float32) {
c.mu.Lock()
defer c.mu.Unlock()
t := float32(v) t := float32(v)
c.config.MicVolume = &t c.config.MicVolume = &t
} }
func (c *Config) GetMicVolume() float32 { func (c *Config) GetMicVolume() float32 {
c.mu.Lock()
defer c.mu.Unlock()
if c.config.MicVolume == nil { if c.config.MicVolume == nil {
return 1.0 return 1.0
} }
@@ -308,6 +322,8 @@ func (c *Config) GetCertificate() *string {
} }
func (c *Config) GetNoiseSuppressionEnabled() bool { func (c *Config) GetNoiseSuppressionEnabled() bool {
c.mu.Lock()
defer c.mu.Unlock()
if c.config.NoiseSuppressionEnabled == nil { if c.config.NoiseSuppressionEnabled == nil {
return false return false
} }
@@ -315,11 +331,15 @@ func (c *Config) GetNoiseSuppressionEnabled() bool {
} }
func (c *Config) SetNoiseSuppressionEnabled(enabled bool) { func (c *Config) SetNoiseSuppressionEnabled(enabled bool) {
c.mu.Lock()
defer c.mu.Unlock()
c.config.NoiseSuppressionEnabled = &enabled c.config.NoiseSuppressionEnabled = &enabled
c.SaveConfig() _ = c.saveConfigLocked()
} }
func (c *Config) GetRecordingFormat() string { func (c *Config) GetRecordingFormat() string {
c.mu.Lock()
defer c.mu.Unlock()
if c.config.RecordingFormat == nil { if c.config.RecordingFormat == nil {
return "flac" return "flac"
} }
@@ -327,6 +347,8 @@ func (c *Config) GetRecordingFormat() string {
} }
func (c *Config) GetRecordingDirectory() string { func (c *Config) GetRecordingDirectory() string {
c.mu.Lock()
defer c.mu.Unlock()
if c.config.RecordingDirectory == nil { if c.config.RecordingDirectory == nil {
return resolvePath("~/Audio") return resolvePath("~/Audio")
} }
@@ -334,6 +356,8 @@ func (c *Config) GetRecordingDirectory() string {
} }
func (c *Config) UpdateUser(u *gumble.User) { func (c *Config) UpdateUser(u *gumble.User) {
c.mu.Lock()
defer c.mu.Unlock()
var j *eUser var j *eUser
var uc *gumble.Client var uc *gumble.Client
uc = u.GetClient() uc = u.GetClient()
@@ -349,6 +373,8 @@ func (c *Config) UpdateUser(u *gumble.User) {
} }
func (c *Config) UpdateConfig(u *gumble.User) { func (c *Config) UpdateConfig(u *gumble.User) {
c.mu.Lock()
defer c.mu.Unlock()
var j *eUser var j *eUser
j = c.findUser(u.GetClient().Config.Address, u.Name) j = c.findUser(u.GetClient().Config.Address, u.Name)
j.Boost = u.Boost() j.Boost = u.Boost()
+18
View File
@@ -2,6 +2,7 @@ package config
import ( import (
"path/filepath" "path/filepath"
"sync"
"testing" "testing"
) )
@@ -14,3 +15,20 @@ func TestSaveConfigReturnsWriteError(t *testing.T) {
t.Fatal("expected configuration write error") t.Fatal("expected configuration write error")
} }
} }
func TestConcurrentConfigurationUpdatesAndWrites(t *testing.T) {
path := filepath.Join(t.TempDir(), "barnard.toml")
cfg := NewConfig(&path)
var wg sync.WaitGroup
for i := 0; i < 20; i++ {
wg.Add(1)
go func(enabled bool) {
defer wg.Done()
cfg.SetNoiseSuppressionEnabled(enabled)
if err := cfg.SaveConfig(); err != nil {
t.Errorf("SaveConfig: %v", err)
}
}(i%2 == 0)
}
wg.Wait()
}