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  }