github.com/docker/engine@v22.0.0-20211208180946-d456264580cf+incompatible/pkg/ioutils/bytespipe.go (about)

     1  package ioutils // import "github.com/docker/docker/pkg/ioutils"
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"sync"
     7  )
     8  
     9  // maxCap is the highest capacity to use in byte slices that buffer data.
    10  const maxCap = 1e6
    11  
    12  // minCap is the lowest capacity to use in byte slices that buffer data
    13  const minCap = 64
    14  
    15  // blockThreshold is the minimum number of bytes in the buffer which will cause
    16  // a write to BytesPipe to block when allocating a new slice.
    17  const blockThreshold = 1e6
    18  
    19  var (
    20  	// ErrClosed is returned when Write is called on a closed BytesPipe.
    21  	ErrClosed = errors.New("write to closed BytesPipe")
    22  
    23  	bufPools     = make(map[int]*sync.Pool)
    24  	bufPoolsLock sync.Mutex
    25  )
    26  
    27  // BytesPipe is io.ReadWriteCloser which works similarly to pipe(queue).
    28  // All written data may be read at most once. Also, BytesPipe allocates
    29  // and releases new byte slices to adjust to current needs, so the buffer
    30  // won't be overgrown after peak loads.
    31  type BytesPipe struct {
    32  	mu       sync.Mutex
    33  	wait     *sync.Cond
    34  	buf      []*fixedBuffer
    35  	bufLen   int
    36  	closeErr error // error to return from next Read. set to nil if not closed.
    37  }
    38  
    39  // NewBytesPipe creates new BytesPipe, initialized by specified slice.
    40  // If buf is nil, then it will be initialized with slice which cap is 64.
    41  // buf will be adjusted in a way that len(buf) == 0, cap(buf) == cap(buf).
    42  func NewBytesPipe() *BytesPipe {
    43  	bp := &BytesPipe{}
    44  	bp.buf = append(bp.buf, getBuffer(minCap))
    45  	bp.wait = sync.NewCond(&bp.mu)
    46  	return bp
    47  }
    48  
    49  // Write writes p to BytesPipe.
    50  // It can allocate new []byte slices in a process of writing.
    51  func (bp *BytesPipe) Write(p []byte) (int, error) {
    52  	bp.mu.Lock()
    53  	defer bp.mu.Unlock()
    54  
    55  	written := 0
    56  loop0:
    57  	for {
    58  		if bp.closeErr != nil {
    59  			return written, ErrClosed
    60  		}
    61  
    62  		if len(bp.buf) == 0 {
    63  			bp.buf = append(bp.buf, getBuffer(64))
    64  		}
    65  		// get the last buffer
    66  		b := bp.buf[len(bp.buf)-1]
    67  
    68  		n, err := b.Write(p)
    69  		written += n
    70  		bp.bufLen += n
    71  
    72  		// errBufferFull is an error we expect to get if the buffer is full
    73  		if err != nil && err != errBufferFull {
    74  			bp.wait.Broadcast()
    75  			return written, err
    76  		}
    77  
    78  		// if there was enough room to write all then break
    79  		if len(p) == n {
    80  			break
    81  		}
    82  
    83  		// more data: write to the next slice
    84  		p = p[n:]
    85  
    86  		// make sure the buffer doesn't grow too big from this write
    87  		for bp.bufLen >= blockThreshold {
    88  			bp.wait.Wait()
    89  			if bp.closeErr != nil {
    90  				continue loop0
    91  			}
    92  		}
    93  
    94  		// add new byte slice to the buffers slice and continue writing
    95  		nextCap := b.Cap() * 2
    96  		if nextCap > maxCap {
    97  			nextCap = maxCap
    98  		}
    99  		bp.buf = append(bp.buf, getBuffer(nextCap))
   100  	}
   101  	bp.wait.Broadcast()
   102  	return written, nil
   103  }
   104  
   105  // CloseWithError causes further reads from a BytesPipe to return immediately.
   106  func (bp *BytesPipe) CloseWithError(err error) error {
   107  	bp.mu.Lock()
   108  	if err != nil {
   109  		bp.closeErr = err
   110  	} else {
   111  		bp.closeErr = io.EOF
   112  	}
   113  	bp.wait.Broadcast()
   114  	bp.mu.Unlock()
   115  	return nil
   116  }
   117  
   118  // Close causes further reads from a BytesPipe to return immediately.
   119  func (bp *BytesPipe) Close() error {
   120  	return bp.CloseWithError(nil)
   121  }
   122  
   123  // Read reads bytes from BytesPipe.
   124  // Data could be read only once.
   125  func (bp *BytesPipe) Read(p []byte) (n int, err error) {
   126  	bp.mu.Lock()
   127  	defer bp.mu.Unlock()
   128  	if bp.bufLen == 0 {
   129  		if bp.closeErr != nil {
   130  			return 0, bp.closeErr
   131  		}
   132  		bp.wait.Wait()
   133  		if bp.bufLen == 0 && bp.closeErr != nil {
   134  			return 0, bp.closeErr
   135  		}
   136  	}
   137  
   138  	for bp.bufLen > 0 {
   139  		b := bp.buf[0]
   140  		read, _ := b.Read(p) // ignore error since fixedBuffer doesn't really return an error
   141  		n += read
   142  		bp.bufLen -= read
   143  
   144  		if b.Len() == 0 {
   145  			// it's empty so return it to the pool and move to the next one
   146  			returnBuffer(b)
   147  			bp.buf[0] = nil
   148  			bp.buf = bp.buf[1:]
   149  		}
   150  
   151  		if len(p) == read {
   152  			break
   153  		}
   154  
   155  		p = p[read:]
   156  	}
   157  
   158  	bp.wait.Broadcast()
   159  	return
   160  }
   161  
   162  func returnBuffer(b *fixedBuffer) {
   163  	b.Reset()
   164  	bufPoolsLock.Lock()
   165  	pool := bufPools[b.Cap()]
   166  	bufPoolsLock.Unlock()
   167  	if pool != nil {
   168  		pool.Put(b)
   169  	}
   170  }
   171  
   172  func getBuffer(size int) *fixedBuffer {
   173  	bufPoolsLock.Lock()
   174  	pool, ok := bufPools[size]
   175  	if !ok {
   176  		pool = &sync.Pool{New: func() interface{} { return &fixedBuffer{buf: make([]byte, 0, size)} }}
   177  		bufPools[size] = pool
   178  	}
   179  	bufPoolsLock.Unlock()
   180  	return pool.Get().(*fixedBuffer)
   181  }