Files
crush/internal/cmd/spawnlock_windows.go
Christian RochaandCharm Crush d41c118511 fix(server): serialize concurrent server spawns with a per-host lock
When two clients started up at the same time they would both try to
spawn a server and one would lose the bind race, leaving a confusing
log behind. Take an exclusive file lock around the spawn-and-wait
sequence, re-check health after acquiring the lock, and skip the spawn
entirely if a peer client has already brought the server up. The lock
is released as soon as the new server is ready.

Co-Authored-By: Charm Crush <crush@charm.land>
2026-05-11 20:24:07 -04:00

33 lines
933 B
Go

//go:build windows
package cmd
import (
"fmt"
"math"
"os"
"golang.org/x/sys/windows"
)
// acquireSpawnLock takes an exclusive lock on the given file (creating
// it if necessary) using LockFileEx, and returns a release function
// that unlocks and closes the file. Blocks until the lock is acquired.
func acquireSpawnLock(path string) (func(), error) {
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0o600)
if err != nil {
return nil, fmt.Errorf("open spawn lock %q: %v", path, err)
}
h := windows.Handle(f.Fd())
ol := new(windows.Overlapped)
if err := windows.LockFileEx(h, windows.LOCKFILE_EXCLUSIVE_LOCK, 0, math.MaxUint32, math.MaxUint32, ol); err != nil {
_ = f.Close()
return nil, fmt.Errorf("LockFileEx spawn lock %q: %v", path, err)
}
return func() {
ol := new(windows.Overlapped)
_ = windows.UnlockFileEx(windows.Handle(f.Fd()), 0, math.MaxUint32, math.MaxUint32, ol)
_ = f.Close()
}, nil
}