Skip to content

Commit 6e7976a

Browse files
znullCopilot
andcommitted
Unwrap nopWriteCloser to expose ReaderFrom
When the pipeline's stdout is set via WithStdout, the writer is wrapped in nopWriteCloser to add a no-op Close method. This wrapper hides the ReaderFrom interface of the underlying writer, preventing io.CopyBuffer from dispatching to it. Fix: unwrap nopWriteCloser in ioCopier and call ReadFrom directly when available. This enables zero-copy when the destination has a meaningful ReadFrom (e.g., network connections, custom writers). For the pipe-to-pipe *os.File case, File.ReadFrom's zero-copy paths don't yet support pipe sources, so a follow-up commit adds direct splice(2) for that case. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent afe1947 commit 6e7976a

1 file changed

Lines changed: 25 additions & 9 deletions

File tree

pipe/iocopier.go

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,31 @@ func (s *ioCopier) Name() string {
4444
// This method always returns `nil, nil`.
4545
func (s *ioCopier) Start(_ context.Context, _ Env, r io.ReadCloser) (io.ReadCloser, error) {
4646
go func() {
47-
bp := copyBufPool.Get().(*[]byte)
48-
// Strip all interfaces except Read from r so that
49-
// io.CopyBuffer always uses the provided pool buffer.
50-
// Without this, *os.File's WriterTo (added in Go 1.26)
51-
// causes CopyBuffer to call File.WriteTo, which falls
52-
// back to io.Copy with a fresh allocation, bypassing
53-
// the pool entirely.
54-
_, err := io.CopyBuffer(s.w, readerOnly{r}, *bp)
55-
copyBufPool.Put(bp)
47+
var err error
48+
49+
// Unwrap nopWriteCloser to see if the underlying writer
50+
// supports ReaderFrom (e.g., for zero-copy network I/O).
51+
var dst io.Writer = s.w
52+
if nwc, ok := s.w.(nopWriteCloser); ok {
53+
dst = nwc.Writer
54+
}
55+
56+
if rf, ok := dst.(io.ReaderFrom); ok {
57+
// Call ReadFrom directly, bypassing io.Copy's
58+
// WriterTo check so that ReadFrom sees the
59+
// original reader type (needed for zero-copy).
60+
_, err = rf.ReadFrom(r)
61+
} else {
62+
bp := copyBufPool.Get().(*[]byte)
63+
// Strip all interfaces except Read from r so that
64+
// io.CopyBuffer always uses the provided pool buffer.
65+
// Without this, *os.File's WriterTo (added in Go 1.26)
66+
// causes CopyBuffer to call File.WriteTo, which falls
67+
// back to io.Copy with a fresh allocation, bypassing
68+
// the pool entirely.
69+
_, err = io.CopyBuffer(dst, readerOnly{r}, *bp)
70+
copyBufPool.Put(bp)
71+
}
5672
// We don't consider `ErrClosed` an error (FIXME: is this
5773
// correct?):
5874
if err != nil && !errors.Is(err, os.ErrClosed) {

0 commit comments

Comments
 (0)