github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/fs/asyncreader/asyncreader.go (about) 1 // Package asyncreader provides an asynchronous reader which reads 2 // independently of write 3 package asyncreader 4 5 import ( 6 "io" 7 "sync" 8 "time" 9 10 "github.com/pkg/errors" 11 "github.com/rclone/rclone/fs" 12 "github.com/rclone/rclone/lib/pool" 13 "github.com/rclone/rclone/lib/readers" 14 ) 15 16 const ( 17 // BufferSize is the default size of the async buffer 18 BufferSize = 1024 * 1024 19 softStartInitial = 4 * 1024 20 bufferCacheSize = 64 // max number of buffers to keep in cache 21 bufferCacheFlushTime = 5 * time.Second // flush the cached buffers after this long 22 ) 23 24 // ErrorStreamAbandoned is returned when the input is closed before the end of the stream 25 var ErrorStreamAbandoned = errors.New("stream abandoned") 26 27 // AsyncReader will do async read-ahead from the input reader 28 // and make the data available as an io.Reader. 29 // This should be fully transparent, except that once an error 30 // has been returned from the Reader, it will not recover. 31 type AsyncReader struct { 32 in io.ReadCloser // Input reader 33 ready chan *buffer // Buffers ready to be handed to the reader 34 token chan struct{} // Tokens which allow a buffer to be taken 35 exit chan struct{} // Closes when finished 36 buffers int // Number of buffers 37 err error // If an error has occurred it is here 38 cur *buffer // Current buffer being served 39 exited chan struct{} // Channel is closed been the async reader shuts down 40 size int // size of buffer to use 41 closed bool // whether we have closed the underlying stream 42 mu sync.Mutex // lock for Read/WriteTo/Abandon/Close 43 } 44 45 // New returns a reader that will asynchronously read from 46 // the supplied Reader into a number of buffers each of size BufferSize 47 // It will start reading from the input at once, maybe even before this 48 // function has returned. 49 // The input can be read from the returned reader. 50 // When done use Close to release the buffers and close the supplied input. 51 func New(rd io.ReadCloser, buffers int) (*AsyncReader, error) { 52 if buffers <= 0 { 53 return nil, errors.New("number of buffers too small") 54 } 55 if rd == nil { 56 return nil, errors.New("nil reader supplied") 57 } 58 a := &AsyncReader{} 59 a.init(rd, buffers) 60 return a, nil 61 } 62 63 func (a *AsyncReader) init(rd io.ReadCloser, buffers int) { 64 a.in = rd 65 a.ready = make(chan *buffer, buffers) 66 a.token = make(chan struct{}, buffers) 67 a.exit = make(chan struct{}, 0) 68 a.exited = make(chan struct{}, 0) 69 a.buffers = buffers 70 a.cur = nil 71 a.size = softStartInitial 72 73 // Create tokens 74 for i := 0; i < buffers; i++ { 75 a.token <- struct{}{} 76 } 77 78 // Start async reader 79 go func() { 80 // Ensure that when we exit this is signalled. 81 defer close(a.exited) 82 defer close(a.ready) 83 for { 84 select { 85 case <-a.token: 86 b := a.getBuffer() 87 if a.size < BufferSize { 88 b.buf = b.buf[:a.size] 89 a.size <<= 1 90 } 91 err := b.read(a.in) 92 a.ready <- b 93 if err != nil { 94 return 95 } 96 case <-a.exit: 97 return 98 } 99 } 100 }() 101 } 102 103 // bufferPool is a global pool of buffers 104 var bufferPool *pool.Pool 105 var bufferPoolOnce sync.Once 106 107 // return the buffer to the pool (clearing it) 108 func (a *AsyncReader) putBuffer(b *buffer) { 109 bufferPool.Put(b.buf) 110 b.buf = nil 111 } 112 113 // get a buffer from the pool 114 func (a *AsyncReader) getBuffer() *buffer { 115 bufferPoolOnce.Do(func() { 116 // Initialise the buffer pool when used 117 bufferPool = pool.New(bufferCacheFlushTime, BufferSize, bufferCacheSize, fs.Config.UseMmap) 118 }) 119 return &buffer{ 120 buf: bufferPool.Get(), 121 } 122 } 123 124 // Read will return the next available data. 125 func (a *AsyncReader) fill() (err error) { 126 if a.cur.isEmpty() { 127 if a.cur != nil { 128 a.putBuffer(a.cur) 129 a.token <- struct{}{} 130 a.cur = nil 131 } 132 b, ok := <-a.ready 133 if !ok { 134 // Return an error to show fill failed 135 if a.err == nil { 136 return ErrorStreamAbandoned 137 } 138 return a.err 139 } 140 a.cur = b 141 } 142 return nil 143 } 144 145 // Read will return the next available data. 146 func (a *AsyncReader) Read(p []byte) (n int, err error) { 147 a.mu.Lock() 148 defer a.mu.Unlock() 149 150 // Swap buffer and maybe return error 151 err = a.fill() 152 if err != nil { 153 return 0, err 154 } 155 156 // Copy what we can 157 n = copy(p, a.cur.buffer()) 158 a.cur.increment(n) 159 160 // If at end of buffer, return any error, if present 161 if a.cur.isEmpty() { 162 a.err = a.cur.err 163 return n, a.err 164 } 165 return n, nil 166 } 167 168 // WriteTo writes data to w until there's no more data to write or when an error occurs. 169 // The return value n is the number of bytes written. 170 // Any error encountered during the write is also returned. 171 func (a *AsyncReader) WriteTo(w io.Writer) (n int64, err error) { 172 a.mu.Lock() 173 defer a.mu.Unlock() 174 175 n = 0 176 for { 177 err = a.fill() 178 if err == io.EOF { 179 return n, nil 180 } 181 if err != nil { 182 return n, err 183 } 184 n2, err := w.Write(a.cur.buffer()) 185 a.cur.increment(n2) 186 n += int64(n2) 187 if err != nil { 188 return n, err 189 } 190 if a.cur.err == io.EOF { 191 a.err = a.cur.err 192 return n, err 193 } 194 if a.cur.err != nil { 195 a.err = a.cur.err 196 return n, a.cur.err 197 } 198 } 199 } 200 201 // SkipBytes will try to seek 'skip' bytes relative to the current position. 202 // On success it returns true. If 'skip' is outside the current buffer data or 203 // an error occurs, Abandon is called and false is returned. 204 func (a *AsyncReader) SkipBytes(skip int) (ok bool) { 205 a.mu.Lock() 206 defer func() { 207 a.mu.Unlock() 208 if !ok { 209 a.Abandon() 210 } 211 }() 212 213 if a.err != nil { 214 return false 215 } 216 if skip < 0 { 217 // seek backwards if skip is inside current buffer 218 if a.cur != nil && a.cur.offset+skip >= 0 { 219 a.cur.offset += skip 220 return true 221 } 222 return false 223 } 224 // early return if skip is past the maximum buffer capacity 225 if skip >= (len(a.ready)+1)*BufferSize { 226 return false 227 } 228 229 refillTokens := 0 230 for { 231 if a.cur.isEmpty() { 232 if a.cur != nil { 233 a.putBuffer(a.cur) 234 refillTokens++ 235 a.cur = nil 236 } 237 select { 238 case b, ok := <-a.ready: 239 if !ok { 240 return false 241 } 242 a.cur = b 243 default: 244 return false 245 } 246 } 247 248 n := len(a.cur.buffer()) 249 if n > skip { 250 n = skip 251 } 252 a.cur.increment(n) 253 skip -= n 254 if skip == 0 { 255 for ; refillTokens > 0; refillTokens-- { 256 a.token <- struct{}{} 257 } 258 // If at end of buffer, store any error, if present 259 if a.cur.isEmpty() && a.cur.err != nil { 260 a.err = a.cur.err 261 } 262 return true 263 } 264 if a.cur.err != nil { 265 a.err = a.cur.err 266 return false 267 } 268 } 269 } 270 271 // Abandon will ensure that the underlying async reader is shut down. 272 // It will NOT close the input supplied on New. 273 func (a *AsyncReader) Abandon() { 274 select { 275 case <-a.exit: 276 // Do nothing if reader routine already exited 277 return 278 default: 279 } 280 // Close and wait for go routine 281 close(a.exit) 282 <-a.exited 283 // take the lock to wait for Read/WriteTo to complete 284 a.mu.Lock() 285 defer a.mu.Unlock() 286 // Return any outstanding buffers to the Pool 287 if a.cur != nil { 288 a.putBuffer(a.cur) 289 a.cur = nil 290 } 291 for b := range a.ready { 292 a.putBuffer(b) 293 } 294 } 295 296 // Close will ensure that the underlying async reader is shut down. 297 // It will also close the input supplied on New. 298 func (a *AsyncReader) Close() (err error) { 299 a.Abandon() 300 if a.closed { 301 return nil 302 } 303 a.closed = true 304 return a.in.Close() 305 } 306 307 // Internal buffer 308 // If an error is present, it must be returned 309 // once all buffer content has been served. 310 type buffer struct { 311 buf []byte 312 err error 313 offset int 314 } 315 316 // isEmpty returns true is offset is at end of 317 // buffer, or 318 func (b *buffer) isEmpty() bool { 319 if b == nil { 320 return true 321 } 322 if len(b.buf)-b.offset <= 0 { 323 return true 324 } 325 return false 326 } 327 328 // read into start of the buffer from the supplied reader, 329 // resets the offset and updates the size of the buffer. 330 // Any error encountered during the read is returned. 331 func (b *buffer) read(rd io.Reader) error { 332 var n int 333 n, b.err = readers.ReadFill(rd, b.buf) 334 b.buf = b.buf[0:n] 335 b.offset = 0 336 return b.err 337 } 338 339 // Return the buffer at current offset 340 func (b *buffer) buffer() []byte { 341 return b.buf[b.offset:] 342 } 343 344 // increment the offset 345 func (b *buffer) increment(n int) { 346 b.offset += n 347 }