Reserve recording paths atomically

This commit is contained in:
Brandon McGinty (chatgpt)
2026-08-10 08:55:25 -04:00
committed by Brandon McGinty
parent 43d7addc15
commit 9fdcf6171d
2 changed files with 58 additions and 1 deletions
+24 -1
View File
@@ -57,14 +57,19 @@ func New(directory string, format string, now time.Time, frameSize int, interval
if err := os.MkdirAll(directory, 0755); err != nil {
return nil, err
}
path := UniquePath(directory, now, format)
path, err := reservePath(directory, now, format)
if err != nil {
return nil, err
}
args := ffmpegArgs(format, path)
cmd := exec.Command("ffmpeg", args...)
stdin, err := cmd.StdinPipe()
if err != nil {
_ = os.Remove(path)
return nil, err
}
if err := cmd.Start(); err != nil {
_ = os.Remove(path)
return nil, err
}
recorder := &Recorder{
@@ -91,6 +96,24 @@ func NormalizeFormat(format string) string {
return format
}
func reservePath(directory string, now time.Time, format string) (string, error) {
for {
path := UniquePath(directory, now, format)
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
if os.IsExist(err) {
continue
}
if err != nil {
return "", err
}
if err := file.Close(); err != nil {
_ = os.Remove(path)
return "", err
}
return path, nil
}
}
func UniquePath(directory string, now time.Time, format string) string {
base := fmt.Sprintf("barnard-recording-%s", now.Format("20060102-150405"))
path := filepath.Join(directory, base+"."+format)
+34
View File
@@ -4,6 +4,7 @@ import (
"io"
"os"
"path/filepath"
"sync"
"testing"
"time"
)
@@ -58,6 +59,39 @@ func TestUniquePathAvoidsCollision(t *testing.T) {
}
}
func TestReservePathPreventsConcurrentRecordingCollisions(t *testing.T) {
dir := t.TempDir()
now := time.Date(2026, 5, 14, 12, 30, 0, 0, time.Local)
paths := make(chan string, 2)
errs := make(chan error, 2)
var wg sync.WaitGroup
for range 2 {
wg.Add(1)
go func() {
defer wg.Done()
path, err := reservePath(dir, now, "flac")
if err != nil {
errs <- err
return
}
paths <- path
}()
}
wg.Wait()
close(paths)
close(errs)
for err := range errs {
t.Fatal(err)
}
var reserved []string
for path := range paths {
reserved = append(reserved, path)
}
if len(reserved) != 2 || reserved[0] == reserved[1] {
t.Fatalf("reserved paths = %#v", reserved)
}
}
func TestNormalizeStereoFrame(t *testing.T) {
mono := NormalizeStereoFrame([]int16{1, -2}, 3)
wantMono := []int16{1, 1, -2, -2, 0, 0}