@@ -3,11 +3,15 @@ package ioutils // import "github.com/docker/docker/pkg/ioutils"
33import (
44 "context"
55 "io"
6+ "runtime/debug"
7+ "sync/atomic"
68
79 // make sure crypto.SHA256, crypto.sha512 and crypto.SHA384 are registered
810 // TODO remove once https://github.com/opencontainers/go-digest/pull/64 is merged.
911 _ "crypto/sha256"
1012 _ "crypto/sha512"
13+
14+ "github.com/sirupsen/logrus"
1115)
1216
1317// ReadCloserWrapper wraps an io.Reader, and implements an io.ReadCloser
@@ -16,10 +20,15 @@ import (
1620type ReadCloserWrapper struct {
1721 io.Reader
1822 closer func () error
23+ closed atomic.Bool
1924}
2025
2126// Close calls back the passed closer function
2227func (r * ReadCloserWrapper ) Close () error {
28+ if ! r .closed .CompareAndSwap (false , true ) {
29+ subsequentCloseWarn ("ReadCloserWrapper" )
30+ return nil
31+ }
2332 return r .closer ()
2433}
2534
@@ -87,6 +96,7 @@ type cancelReadCloser struct {
8796 cancel func ()
8897 pR * io.PipeReader // Stream to read from
8998 pW * io.PipeWriter
99+ closed atomic.Bool
90100}
91101
92102// NewCancelReadCloser creates a wrapper that closes the ReadCloser when the
@@ -146,6 +156,17 @@ func (p *cancelReadCloser) closeWithError(err error) {
146156// Close closes the wrapper its underlying reader. It will cause
147157// future calls to Read to return io.EOF.
148158func (p * cancelReadCloser ) Close () error {
159+ if ! p .closed .CompareAndSwap (false , true ) {
160+ subsequentCloseWarn ("cancelReadCloser" )
161+ return nil
162+ }
149163 p .closeWithError (io .EOF )
150164 return nil
151165}
166+
167+ func subsequentCloseWarn (name string ) {
168+ logrus .Error ("subsequent attempt to close " + name )
169+ if logrus .GetLevel () >= logrus .DebugLevel {
170+ logrus .Errorf ("stack trace: %s" , string (debug .Stack ()))
171+ }
172+ }
0 commit comments