Skip to content

Commit

Permalink
fix: make writeAppState atomic
Browse files Browse the repository at this point in the history
This makes sure the file is never half-written and won't be written to
by two processes at the same time.
  • Loading branch information
aykevl authored and devgianlu committed Oct 1, 2024
1 parent a6062d6 commit 9dc93f6
Showing 1 changed file with 18 additions and 1 deletion.
19 changes: 18 additions & 1 deletion cmd/daemon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os"
"path/filepath"
"strings"
"sync"
"time"

"github.com/devgianlu/go-librespot/apresolve"
Expand Down Expand Up @@ -41,6 +42,7 @@ type App struct {
deviceType devicespb.DeviceType
clientToken string
state *AppState
stateLock sync.Mutex

server *ApiServer
logoutCh chan *AppPlayer
Expand Down Expand Up @@ -515,13 +517,28 @@ func (app *App) readAppState() error {
}

func (app *App) writeAppState() error {
app.stateLock.Lock()
defer app.stateLock.Unlock()

content, err := json.MarshalIndent(&app.state, "", "\t")
if err != nil {
return fmt.Errorf("failed marshalling app state: %w", err)
}
if err := os.WriteFile(app.cfg.StatePath(), content, 0600); err != nil {

// Create a temporary file, and overwrite the old file.
// This is a way to atomically replace files.
// The file is created with mode 0o600 so we don't need to change the mode.
tmppath := app.cfg.StatePath()
tmpfile, err := os.CreateTemp(filepath.Dir(tmppath), filepath.Base(tmppath)+".*.tmp")
if err != nil {
return fmt.Errorf("failed creating temporary file for app state: %w", err)
}
if _, err := tmpfile.Write(content); err != nil {
return fmt.Errorf("failed writing app state: %w", err)
}
if err := os.Rename(tmpfile.Name(), tmppath); err != nil {
return fmt.Errorf("failed replacing app state file: %w", err)
}
return nil
}

Expand Down

0 comments on commit 9dc93f6

Please sign in to comment.