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 }