//go:build unix package squad import ( "context" "os" "path/filepath" "syscall" "time" ) // lockRunFile 对 runs//.lock 做非阻塞独占锁,并按 ctx 轮询。 // 直到拿到锁或 ctx 结束。ctx 结束时返回 ErrRunBusy。 func lockRunFile(ctx context.Context, dir string) (func(), error) { if err := os.MkdirAll(dir, 0o755); err != nil { return nil, err } f, err := os.OpenFile(filepath.Join(dir, ".lock"), os.O_CREATE|os.O_RDWR, 0o644) if err != nil { return nil, err } for { err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB) if err == nil { return func() { _ = syscall.Flock(int(f.Fd()), syscall.LOCK_UN) _ = f.Close() }, nil } if err != syscall.EWOULDBLOCK && err != syscall.EAGAIN { _ = f.Close() return nil, err } select { case <-ctx.Done(): _ = f.Close() return nil, ErrRunBusy case <-time.After(20 * time.Millisecond): } } }