github.com/toplink-cn/moby@v0.0.0-20240305205811-460b4aebdf81/pkg/ioutils/readers.go (about) 1 package ioutils // import "github.com/docker/docker/pkg/ioutils" 2 3 import ( 4 "context" 5 "io" 6 "runtime/debug" 7 "sync/atomic" 8 9 // make sure crypto.SHA256, crypto.sha512 and crypto.SHA384 are registered 10 // TODO remove once https://github.com/opencontainers/go-digest/pull/64 is merged. 11 _ "crypto/sha256" 12 _ "crypto/sha512" 13 14 "github.com/containerd/log" 15 ) 16 17 // ReadCloserWrapper wraps an io.Reader, and implements an io.ReadCloser 18 // It calls the given callback function when closed. It should be constructed 19 // with NewReadCloserWrapper 20 type ReadCloserWrapper struct { 21 io.Reader 22 closer func() error 23 closed atomic.Bool 24 } 25 26 // Close calls back the passed closer function 27 func (r *ReadCloserWrapper) Close() error { 28 if !r.closed.CompareAndSwap(false, true) { 29 subsequentCloseWarn("ReadCloserWrapper") 30 return nil 31 } 32 return r.closer() 33 } 34 35 // NewReadCloserWrapper returns a new io.ReadCloser. 36 func NewReadCloserWrapper(r io.Reader, closer func() error) io.ReadCloser { 37 return &ReadCloserWrapper{ 38 Reader: r, 39 closer: closer, 40 } 41 } 42 43 type readerErrWrapper struct { 44 reader io.Reader 45 closer func() 46 } 47 48 func (r *readerErrWrapper) Read(p []byte) (int, error) { 49 n, err := r.reader.Read(p) 50 if err != nil { 51 r.closer() 52 } 53 return n, err 54 } 55 56 // NewReaderErrWrapper returns a new io.Reader. 57 func NewReaderErrWrapper(r io.Reader, closer func()) io.Reader { 58 return &readerErrWrapper{ 59 reader: r, 60 closer: closer, 61 } 62 } 63 64 // OnEOFReader wraps an io.ReadCloser and a function 65 // the function will run at the end of file or close the file. 66 type OnEOFReader struct { 67 Rc io.ReadCloser 68 Fn func() 69 } 70 71 func (r *OnEOFReader) Read(p []byte) (n int, err error) { 72 n, err = r.Rc.Read(p) 73 if err == io.EOF { 74 r.runFunc() 75 } 76 return 77 } 78 79 // Close closes the file and run the function. 80 func (r *OnEOFReader) Close() error { 81 err := r.Rc.Close() 82 r.runFunc() 83 return err 84 } 85 86 func (r *OnEOFReader) runFunc() { 87 if fn := r.Fn; fn != nil { 88 fn() 89 r.Fn = nil 90 } 91 } 92 93 // cancelReadCloser wraps an io.ReadCloser with a context for cancelling read 94 // operations. 95 type cancelReadCloser struct { 96 cancel func() 97 pR *io.PipeReader // Stream to read from 98 pW *io.PipeWriter 99 closed atomic.Bool 100 } 101 102 // NewCancelReadCloser creates a wrapper that closes the ReadCloser when the 103 // context is cancelled. The returned io.ReadCloser must be closed when it is 104 // no longer needed. 105 func NewCancelReadCloser(ctx context.Context, in io.ReadCloser) io.ReadCloser { 106 pR, pW := io.Pipe() 107 108 // Create a context used to signal when the pipe is closed 109 doneCtx, cancel := context.WithCancel(context.Background()) 110 111 p := &cancelReadCloser{ 112 cancel: cancel, 113 pR: pR, 114 pW: pW, 115 } 116 117 go func() { 118 _, err := io.Copy(pW, in) 119 select { 120 case <-ctx.Done(): 121 // If the context was closed, p.closeWithError 122 // was already called. Calling it again would 123 // change the error that Read returns. 124 default: 125 p.closeWithError(err) 126 } 127 in.Close() 128 }() 129 go func() { 130 for { 131 select { 132 case <-ctx.Done(): 133 p.closeWithError(ctx.Err()) 134 case <-doneCtx.Done(): 135 return 136 } 137 } 138 }() 139 140 return p 141 } 142 143 // Read wraps the Read method of the pipe that provides data from the wrapped 144 // ReadCloser. 145 func (p *cancelReadCloser) Read(buf []byte) (n int, err error) { 146 return p.pR.Read(buf) 147 } 148 149 // closeWithError closes the wrapper and its underlying reader. It will 150 // cause future calls to Read to return err. 151 func (p *cancelReadCloser) closeWithError(err error) { 152 p.pW.CloseWithError(err) 153 p.cancel() 154 } 155 156 // Close closes the wrapper its underlying reader. It will cause 157 // future calls to Read to return io.EOF. 158 func (p *cancelReadCloser) Close() error { 159 if !p.closed.CompareAndSwap(false, true) { 160 subsequentCloseWarn("cancelReadCloser") 161 return nil 162 } 163 p.closeWithError(io.EOF) 164 return nil 165 } 166 167 func subsequentCloseWarn(name string) { 168 log.G(context.TODO()).Error("subsequent attempt to close " + name) 169 if log.GetLevel() >= log.DebugLevel { 170 log.G(context.TODO()).Errorf("stack trace: %s", string(debug.Stack())) 171 } 172 }