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