From e309d22137e542643357ebc941e1784adf8e490e Mon Sep 17 00:00:00 2001 From: "Brandon McGinty (chatgpt)" Date: Sun, 9 Aug 2026 14:50:29 -0400 Subject: [PATCH] Let recorder worker close encoder stdin --- fix.txt | 2 +- recording/recorder.go | 5 ++--- recording/recorder_test.go | 23 +++++++++++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/fix.txt b/fix.txt index 61f36c1..aba5f8d 100644 --- a/fix.txt +++ b/fix.txt @@ -108,7 +108,7 @@ Priority 1: transport, lifecycle, and correctness context, stop channel, output channel, and WaitGroup. Stop must cancel and join that session before another begins. -13. Recorder Stop races the encoder writer +[x] 13. Recorder Stop races the encoder writer File: recording/recorder.go Stop closes stdin while run may be writing. A normal stop can therefore record a closed-pipe error and be reported as failed. Have run own stdin diff --git a/recording/recorder.go b/recording/recorder.go index 5346c01..4f40dbe 100644 --- a/recording/recorder.go +++ b/recording/recorder.go @@ -128,10 +128,9 @@ func (r *Recorder) Stop() error { return nil } r.once.Do(func() { + // run owns stdin and closes it only after it has stopped writing. + // Closing it here races writePCM and turns a normal stop into EPIPE. close(r.stop) - if r.stdin != nil { - r.stdin.Close() - } }) select { case <-r.done: diff --git a/recording/recorder_test.go b/recording/recorder_test.go index 130d522..70a8e04 100644 --- a/recording/recorder_test.go +++ b/recording/recorder_test.go @@ -1,12 +1,35 @@ package recording import ( + "io" "os" "path/filepath" "testing" "time" ) +type trackingWriteCloser struct{ closed bool } + +func (w *trackingWriteCloser) Write([]byte) (int, error) { return 0, nil } +func (w *trackingWriteCloser) Close() error { w.closed = true; return nil } + +var _ io.WriteCloser = (*trackingWriteCloser)(nil) + +// Regression: Stop closed ffmpeg stdin while the worker could still write, +// creating a spurious closed-pipe recording failure. +func TestStopLeavesEncoderClosureToWorker(t *testing.T) { + stdin := &trackingWriteCloser{} + done := make(chan struct{}) + close(done) + r := &Recorder{stdin: stdin, stop: make(chan struct{}), done: done} + if err := r.Stop(); err != nil { + t.Fatal(err) + } + if stdin.closed { + t.Fatal("Stop closed stdin instead of the worker") + } +} + func TestNormalizeFormat(t *testing.T) { tests := map[string]string{ "": "flac",