fix(backend): replace mutex with atomic state machine in ProcessManager and SteamCmdManager

- ProcessManager: atomic.Int32 state enum (idle/starting/running/stopping)
  eliminates race between IsRunning check and Start/Stop
- SteamCmdManager: atomic.Bool with CompareAndSwap for same protection
- Restart() now propagates Stop() errors instead of swallowing them
This commit is contained in:
MrFastwind
2026-07-23 20:15:11 +02:00
parent 9f232b5225
commit 6e628a0bba
2 changed files with 40 additions and 30 deletions
+9 -20
View File
@@ -6,7 +6,7 @@ import (
"os"
"os/exec"
"path/filepath"
"sync"
"sync/atomic"
"time"
)
@@ -24,9 +24,8 @@ const arma3AppID = "233780"
type SteamCmdManager struct {
serverfileDir string
streamer *LogStreamer
mu sync.Mutex
running atomic.Bool
cancel context.CancelFunc
running bool
}
func NewSteamCmdManager(serverfileDir string, streamer *LogStreamer) *SteamCmdManager {
@@ -37,18 +36,13 @@ func NewSteamCmdManager(serverfileDir string, streamer *LogStreamer) *SteamCmdMa
}
func (s *SteamCmdManager) IsRunning() bool {
s.mu.Lock()
defer s.mu.Unlock()
return s.running
return s.running.Load()
}
func (s *SteamCmdManager) UpdateGame(branch, user string) error {
s.mu.Lock()
if s.running {
s.mu.Unlock()
if !s.running.CompareAndSwap(false, true) {
return fmt.Errorf("steamcmd already running")
}
s.mu.Unlock()
args := []string{
"+force_install_dir", s.serverfileDir,
@@ -68,12 +62,9 @@ func (s *SteamCmdManager) DownloadMod(modID string) error {
}
func (s *SteamCmdManager) DownloadMods(modIDs []string) error {
s.mu.Lock()
if s.running {
s.mu.Unlock()
if !s.running.CompareAndSwap(false, true) {
return fmt.Errorf("steamcmd already running")
}
s.mu.Unlock()
if len(modIDs) == 0 {
return fmt.Errorf("no mod ids provided")
@@ -104,33 +95,31 @@ func (s *SteamCmdManager) run(label string, args []string) error {
stdout, err := cmd.StdoutPipe()
if err != nil {
cancel()
s.running.Store(false)
return fmt.Errorf("stdout pipe: %w", err)
}
stderr, err := cmd.StderrPipe()
if err != nil {
cancel()
s.running.Store(false)
return fmt.Errorf("stderr pipe: %w", err)
}
if err := cmd.Start(); err != nil {
cancel()
s.running.Store(false)
return fmt.Errorf("start steamcmd: %w", err)
}
s.mu.Lock()
s.running = true
s.cancel = cancel
s.mu.Unlock()
go s.streamer.Stream("steamcmd", stdout, "")
go s.streamer.Stream("steamcmd", stderr, "")
go func() {
err := cmd.Wait()
s.mu.Lock()
s.running = false
s.running.Store(false)
s.cancel = nil
s.mu.Unlock()
if err == nil {
s.streamer.Broadcast("steamcmd", "[STEAMCMD] SUCCESS: "+label+" finished")
} else {