浏览代码

pkg/ioutils: Make subsequent Close attempts noop

Turn subsequent `Close` calls into a no-op and produce a warning with an
optional stack trace (if debug mode is enabled).

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
(cherry picked from commit 585d74bad162e0946d17808e316d10e60cb9ac59)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
Paweł Gronowski 1 年之前
父节点
当前提交
c01bbbddeb
共有 2 个文件被更改,包括 30 次插入1 次删除
  1. 21 0
      pkg/ioutils/readers.go
  2. 9 1
      pkg/ioutils/writers.go

+ 21 - 0
pkg/ioutils/readers.go

@@ -3,11 +3,15 @@ package ioutils // import "github.com/docker/docker/pkg/ioutils"
 import (
 	"context"
 	"io"
+	"runtime/debug"
+	"sync/atomic"
 
 	// make sure crypto.SHA256, crypto.sha512 and crypto.SHA384 are registered
 	// TODO remove once https://github.com/opencontainers/go-digest/pull/64 is merged.
 	_ "crypto/sha256"
 	_ "crypto/sha512"
+
+	"github.com/containerd/log"
 )
 
 // ReadCloserWrapper wraps an io.Reader, and implements an io.ReadCloser
@@ -16,10 +20,15 @@ import (
 type ReadCloserWrapper struct {
 	io.Reader
 	closer func() error
+	closed atomic.Bool
 }
 
 // Close calls back the passed closer function
 func (r *ReadCloserWrapper) Close() error {
+	if !r.closed.CompareAndSwap(false, true) {
+		subsequentCloseWarn("ReadCloserWrapper")
+		return nil
+	}
 	return r.closer()
 }
 
@@ -87,6 +96,7 @@ type cancelReadCloser struct {
 	cancel func()
 	pR     *io.PipeReader // Stream to read from
 	pW     *io.PipeWriter
+	closed atomic.Bool
 }
 
 // NewCancelReadCloser creates a wrapper that closes the ReadCloser when the
@@ -146,6 +156,17 @@ func (p *cancelReadCloser) closeWithError(err error) {
 // Close closes the wrapper its underlying reader. It will cause
 // future calls to Read to return io.EOF.
 func (p *cancelReadCloser) Close() error {
+	if !p.closed.CompareAndSwap(false, true) {
+		subsequentCloseWarn("cancelReadCloser")
+		return nil
+	}
 	p.closeWithError(io.EOF)
 	return nil
 }
+
+func subsequentCloseWarn(name string) {
+	log.G(context.TODO()).Error("subsequent attempt to close " + name)
+	if log.GetLevel() >= log.DebugLevel {
+		log.G(context.TODO()).Errorf("stack trace: %s", string(debug.Stack()))
+	}
+}

+ 9 - 1
pkg/ioutils/writers.go

@@ -1,6 +1,9 @@
 package ioutils // import "github.com/docker/docker/pkg/ioutils"
 
-import "io"
+import (
+	"io"
+	"sync/atomic"
+)
 
 // NopWriter represents a type which write operation is nop.
 type NopWriter struct{}
@@ -29,9 +32,14 @@ func (f *NopFlusher) Flush() {}
 type writeCloserWrapper struct {
 	io.Writer
 	closer func() error
+	closed atomic.Bool
 }
 
 func (r *writeCloserWrapper) Close() error {
+	if !r.closed.CompareAndSwap(false, true) {
+		subsequentCloseWarn("WriteCloserWrapper")
+		return nil
+	}
 	return r.closer()
 }