Skip to content

Commit 60b7649

Browse files
committed
runControllerBuild: Read from stdin only when needed
Current forwarder implementation aggressively reads from the reader registered via SetReader. And in runControllerBuild, stdin is always registered to the forwarder by default. But there are cases where stdin should not be read so this commit fix this behaviour to register the stdin reader only when actual read happens. Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
1 parent 78adfc8 commit 60b7649

1 file changed

Lines changed: 39 additions & 8 deletions

File tree

commands/build.go

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313
"path/filepath"
1414
"strconv"
1515
"strings"
16+
"sync"
17+
"sync/atomic"
1618

1719
"github.com/containerd/console"
1820
"github.com/docker/buildx/build"
@@ -368,15 +370,22 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
368370
var retErr error
369371
var resp *client.SolveResponse
370372
f := ioset.NewSingleForwarder()
371-
f.SetReader(dockerCli.In())
372373
pr, pw := io.Pipe()
373-
f.SetWriter(pw, func() io.WriteCloser {
374-
pw.Close() // propagate EOF
375-
logrus.Debug("propagating stdin close")
376-
return nil
377-
})
374+
isForwardStarted := false
375+
prReader := &readCloserWithCallback{
376+
rc: pr,
377+
onRead: func() {
378+
f.SetWriter(pw, func() io.WriteCloser {
379+
pw.Close() // propagate EOF
380+
logrus.Debug("propagating stdin close")
381+
return nil
382+
})
383+
f.SetReader(dockerCli.In())
384+
isForwardStarted = true
385+
},
386+
}
378387

379-
ref, resp, err = c.Build(ctx, *opts, pr, printer)
388+
ref, resp, err = c.Build(ctx, *opts, prReader, printer)
380389
if err != nil {
381390
var be *controllererrors.BuildError
382391
if errors.As(err, &be) {
@@ -391,7 +400,7 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
391400
if err := pw.Close(); err != nil {
392401
logrus.Debug("failed to close stdin pipe writer")
393402
}
394-
if err := pr.Close(); err != nil {
403+
if err := prReader.Close(); err != nil {
395404
logrus.Debug("failed to close stdin pipe reader")
396405
}
397406

@@ -401,6 +410,9 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
401410
logrus.Warnf("failed to print error information: %v", err)
402411
}
403412

413+
if !isForwardStarted {
414+
f.SetReader(dockerCli.In())
415+
}
404416
pr2, pw2 := io.Pipe()
405417
f.SetWriter(pw2, func() io.WriteCloser {
406418
pw2.Close() // propagate EOF
@@ -967,3 +979,22 @@ func recordVersionInfo(mp metric.MeterProvider, command string) {
967979
),
968980
)
969981
}
982+
983+
type readCloserWithCallback struct {
984+
rc io.ReadCloser
985+
onRead func()
986+
closed atomic.Bool
987+
onReadOnce sync.Once
988+
}
989+
990+
func (r *readCloserWithCallback) Read(p []byte) (int, error) {
991+
if !r.closed.Load() && r.onRead != nil {
992+
r.onReadOnce.Do(r.onRead)
993+
}
994+
return r.rc.Read(p)
995+
}
996+
997+
func (r *readCloserWithCallback) Close() error {
998+
r.closed.Store(true)
999+
return r.rc.Close()
1000+
}

0 commit comments

Comments
 (0)