github.com/t2y/goofys@v0.19.1-0.20190123053037-27053313e616/internal/buffer_pool.go (about)

     1  // Copyright 2015 - 2017 Ka-Hing Cheung
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package internal
    16  
    17  import (
    18  	"io"
    19  	"runtime"
    20  	"runtime/debug"
    21  	"sync"
    22  
    23  	"github.com/jacobsa/fuse"
    24  	"github.com/shirou/gopsutil/mem"
    25  )
    26  
    27  type BufferPool struct {
    28  	mu   sync.Mutex
    29  	cond *sync.Cond
    30  
    31  	numBuffers uint64
    32  	maxBuffers uint64
    33  
    34  	totalBuffers       uint64
    35  	computedMaxbuffers uint64
    36  
    37  	pool *sync.Pool
    38  }
    39  
    40  const BUF_SIZE = 5 * 1024 * 1024
    41  
    42  func maxMemToUse(buffersNow uint64) uint64 {
    43  	m, err := mem.VirtualMemory()
    44  	if err != nil {
    45  		panic(err)
    46  	}
    47  
    48  	log.Debugf("amount of available memory: %v", m.Available/1024/1024)
    49  
    50  	var ms runtime.MemStats
    51  	runtime.ReadMemStats(&ms)
    52  
    53  	log.Debugf("amount of allocated memory: %v %v", ms.Sys/1024/1024, ms.Alloc/1024/1024)
    54  
    55  	max := uint64(m.Available+ms.Sys) / 2
    56  	maxbuffers := MaxUInt64(max/BUF_SIZE, 1)
    57  	log.Debugf("using up to %v %vMB buffers, now is %v", maxbuffers, BUF_SIZE/1024/1024, buffersNow)
    58  	return maxbuffers
    59  }
    60  
    61  func rounduUp(size uint64, pageSize int) int {
    62  	return pages(size, pageSize) * pageSize
    63  }
    64  
    65  func pages(size uint64, pageSize int) int {
    66  	return int((size + uint64(pageSize) - 1) / uint64(pageSize))
    67  }
    68  
    69  func (pool BufferPool) Init() *BufferPool {
    70  	pool.cond = sync.NewCond(&pool.mu)
    71  
    72  	pool.computedMaxbuffers = pool.maxBuffers
    73  	pool.pool = &sync.Pool{New: func() interface{} {
    74  		return make([]byte, 0, BUF_SIZE)
    75  	}}
    76  
    77  	return &pool
    78  }
    79  
    80  // for testing
    81  func NewBufferPool(maxSizeGlobal uint64) *BufferPool {
    82  	pool := BufferPool{maxBuffers: maxSizeGlobal / BUF_SIZE}.Init()
    83  	return pool
    84  }
    85  
    86  func (pool *BufferPool) RequestBuffer() (buf []byte) {
    87  	return pool.RequestMultiple(BUF_SIZE, true)[0]
    88  }
    89  
    90  func (pool *BufferPool) recomputeBufferLimit() {
    91  	if pool.maxBuffers == 0 {
    92  		pool.computedMaxbuffers = maxMemToUse(pool.numBuffers)
    93  		if pool.computedMaxbuffers == 0 {
    94  			panic("OOM")
    95  		}
    96  	}
    97  }
    98  
    99  func (pool *BufferPool) RequestMultiple(size uint64, block bool) (buffers [][]byte) {
   100  	nPages := pages(size, BUF_SIZE)
   101  
   102  	pool.mu.Lock()
   103  	defer pool.mu.Unlock()
   104  
   105  	if pool.totalBuffers%10 == 0 {
   106  		pool.recomputeBufferLimit()
   107  	}
   108  
   109  	for pool.numBuffers+uint64(nPages) > pool.computedMaxbuffers {
   110  		if block {
   111  			if pool.numBuffers == 0 {
   112  				pool.MaybeGC()
   113  				pool.recomputeBufferLimit()
   114  				if pool.numBuffers+uint64(nPages) > pool.computedMaxbuffers {
   115  					// we don't have any in use buffers, and we've made attempts to
   116  					// free memory AND correct our limits, yet we still can't allocate.
   117  					// it's likely that we are simply asking for too much
   118  					log.Errorf("Unable to allocate %d bytes, limit is %d bytes",
   119  						nPages*BUF_SIZE, pool.computedMaxbuffers*BUF_SIZE)
   120  					panic("OOM")
   121  				}
   122  			}
   123  			pool.cond.Wait()
   124  		} else {
   125  			return
   126  		}
   127  	}
   128  
   129  	for i := 0; i < nPages; i++ {
   130  		pool.numBuffers++
   131  		pool.totalBuffers++
   132  		buf := pool.pool.Get()
   133  		buffers = append(buffers, buf.([]byte))
   134  	}
   135  	return
   136  }
   137  
   138  func (pool *BufferPool) MaybeGC() {
   139  	if pool.numBuffers == 0 {
   140  		debug.FreeOSMemory()
   141  	}
   142  }
   143  
   144  func (pool *BufferPool) Free(buf []byte) {
   145  	pool.mu.Lock()
   146  	defer pool.mu.Unlock()
   147  
   148  	buf = buf[:0]
   149  	pool.pool.Put(buf)
   150  	pool.numBuffers--
   151  	pool.cond.Signal()
   152  }
   153  
   154  var mbufLog = GetLogger("mbuf")
   155  
   156  type MBuf struct {
   157  	pool    *BufferPool
   158  	buffers [][]byte
   159  	rbuf    int
   160  	wbuf    int
   161  	rp      int
   162  	wp      int
   163  }
   164  
   165  func (mb MBuf) Init(h *BufferPool, size uint64, block bool) *MBuf {
   166  	mb.pool = h
   167  
   168  	if size != 0 {
   169  		mb.buffers = h.RequestMultiple(size, block)
   170  		if mb.buffers == nil {
   171  			return nil
   172  		}
   173  	}
   174  
   175  	return &mb
   176  }
   177  
   178  func (mb *MBuf) Len() (length int) {
   179  	for i := mb.rbuf; i < int(len(mb.buffers)); i++ {
   180  		var bufSize int
   181  		var start int
   182  		if i == mb.wbuf {
   183  			bufSize = mb.wp
   184  		} else {
   185  			bufSize = int(len(mb.buffers[i]))
   186  		}
   187  
   188  		if i == mb.rbuf {
   189  			start = mb.rp
   190  		} else {
   191  			start = 0
   192  		}
   193  
   194  		length += bufSize - start
   195  	}
   196  
   197  	return
   198  }
   199  
   200  // seek only seeks the reader
   201  func (mb *MBuf) Seek(offset int64, whence int) (int64, error) {
   202  	switch whence {
   203  	case 0: // relative to beginning
   204  		if offset == 0 {
   205  			mb.rbuf = 0
   206  			mb.rp = 0
   207  			return 0, nil
   208  		}
   209  	case 1: // relative to current position
   210  		if offset == 0 {
   211  			for i := 0; i < mb.rbuf; i++ {
   212  				offset += int64(len(mb.buffers[i]))
   213  			}
   214  			offset += int64(mb.rp)
   215  			return offset, nil
   216  		}
   217  
   218  	case 2: // relative to the end
   219  		if offset == 0 {
   220  			for i := 0; i < len(mb.buffers); i++ {
   221  				offset += int64(len(mb.buffers[i]))
   222  			}
   223  			mb.rbuf = len(mb.buffers)
   224  			mb.rp = 0
   225  			return offset, nil
   226  		}
   227  	}
   228  
   229  	log.Errorf("Seek %d %d", offset, whence)
   230  	panic(fuse.EINVAL)
   231  
   232  	return 0, fuse.EINVAL
   233  }
   234  
   235  func (mb *MBuf) Read(p []byte) (n int, err error) {
   236  	if mb.rbuf == mb.wbuf && mb.rp == mb.wp {
   237  		err = io.EOF
   238  		return
   239  	}
   240  
   241  	if mb.rp == cap(mb.buffers[mb.rbuf]) {
   242  		mb.rbuf++
   243  		mb.rp = 0
   244  	}
   245  
   246  	if mb.rbuf == len(mb.buffers) {
   247  		err = io.EOF
   248  		return
   249  	} else if mb.rbuf > len(mb.buffers) {
   250  		panic("mb.cur > len(mb.buffers)")
   251  	}
   252  
   253  	n = copy(p, mb.buffers[mb.rbuf][mb.rp:])
   254  	mb.rp += n
   255  
   256  	return
   257  }
   258  
   259  func (mb *MBuf) Full() bool {
   260  	return mb.buffers == nil || (mb.wp == cap(mb.buffers[mb.wbuf]) && mb.wbuf+1 == len(mb.buffers))
   261  }
   262  
   263  func (mb *MBuf) Write(p []byte) (n int, err error) {
   264  	b := mb.buffers[mb.wbuf]
   265  
   266  	if mb.wp == cap(b) {
   267  		if mb.wbuf+1 == len(mb.buffers) {
   268  			return
   269  		}
   270  		mb.wbuf++
   271  		b = mb.buffers[mb.wbuf]
   272  		mb.wp = 0
   273  	} else if mb.wp > cap(b) {
   274  		panic("mb.wp > cap(b)")
   275  	}
   276  
   277  	n = copy(b[mb.wp:cap(b)], p)
   278  	mb.wp += n
   279  	// resize the buffer to account for what we just read
   280  	mb.buffers[mb.wbuf] = mb.buffers[mb.wbuf][:mb.wp]
   281  
   282  	return
   283  }
   284  
   285  func (mb *MBuf) WriteFrom(r io.Reader) (n int, err error) {
   286  	b := mb.buffers[mb.wbuf]
   287  
   288  	if mb.wp == cap(b) {
   289  		if mb.wbuf+1 == len(mb.buffers) {
   290  			return
   291  		}
   292  		mb.wbuf++
   293  		b = mb.buffers[mb.wbuf]
   294  		mb.wp = 0
   295  	} else if mb.wp > cap(b) {
   296  		panic("mb.wp > cap(b)")
   297  	}
   298  
   299  	n, err = r.Read(b[mb.wp:cap(b)])
   300  	mb.wp += n
   301  	// resize the buffer to account for what we just read
   302  	mb.buffers[mb.wbuf] = mb.buffers[mb.wbuf][:mb.wp]
   303  
   304  	return
   305  }
   306  
   307  func (mb *MBuf) Free() {
   308  	for _, b := range mb.buffers {
   309  		mb.pool.Free(b)
   310  	}
   311  
   312  	mb.buffers = nil
   313  }
   314  
   315  var bufferLog = GetLogger("buffer")
   316  
   317  type Buffer struct {
   318  	mu   sync.Mutex
   319  	cond *sync.Cond
   320  
   321  	buf    *MBuf
   322  	reader io.ReadCloser
   323  	err    error
   324  }
   325  
   326  type ReaderProvider func() (io.ReadCloser, error)
   327  
   328  func (b Buffer) Init(buf *MBuf, r ReaderProvider) *Buffer {
   329  	b.buf = buf
   330  	b.cond = sync.NewCond(&b.mu)
   331  
   332  	go func() {
   333  		b.readLoop(r)
   334  	}()
   335  
   336  	return &b
   337  }
   338  
   339  func (b *Buffer) readLoop(r ReaderProvider) {
   340  	for {
   341  		b.mu.Lock()
   342  		if b.reader == nil {
   343  			b.reader, b.err = r()
   344  			b.cond.Broadcast()
   345  			if b.err != nil {
   346  				b.mu.Unlock()
   347  				break
   348  			}
   349  		}
   350  
   351  		if b.buf == nil {
   352  			// buffer was drained
   353  			b.mu.Unlock()
   354  			break
   355  		}
   356  
   357  		nread, err := b.buf.WriteFrom(b.reader)
   358  		if err != nil {
   359  			b.err = err
   360  			b.mu.Unlock()
   361  			break
   362  		}
   363  		bufferLog.Debugf("wrote %v into buffer", nread)
   364  
   365  		if nread == 0 {
   366  			b.reader.Close()
   367  			b.mu.Unlock()
   368  			break
   369  		}
   370  
   371  		b.mu.Unlock()
   372  		// if we get here we've read _something_, bounce this goroutine
   373  		// to allow another one to read
   374  		runtime.Gosched()
   375  	}
   376  	bufferLog.Debugf("<-- readLoop()")
   377  }
   378  
   379  func (b *Buffer) readFromStream(p []byte) (n int, err error) {
   380  	bufferLog.Debugf("reading %v from stream", len(p))
   381  
   382  	n, err = b.reader.Read(p)
   383  	if n != 0 && err == io.ErrUnexpectedEOF {
   384  		err = nil
   385  	} else {
   386  		bufferLog.Debugf("read %v from stream", n)
   387  	}
   388  	return
   389  }
   390  
   391  func (b *Buffer) Read(p []byte) (n int, err error) {
   392  	bufferLog.Debugf("Buffer.Read(%v)", len(p))
   393  
   394  	b.mu.Lock()
   395  	defer b.mu.Unlock()
   396  
   397  	for b.reader == nil && b.err == nil {
   398  		bufferLog.Debugf("waiting for stream")
   399  		b.cond.Wait()
   400  		if b.err != nil {
   401  			err = b.err
   402  			return
   403  		}
   404  	}
   405  
   406  	// we could have received the err before Read was called
   407  	if b.reader == nil {
   408  		if b.err == nil {
   409  			panic("reader and err are both nil")
   410  		}
   411  		err = b.err
   412  		return
   413  	}
   414  
   415  	if b.buf != nil {
   416  		bufferLog.Debugf("reading %v from buffer", len(p))
   417  
   418  		n, err = b.buf.Read(p)
   419  		if n == 0 {
   420  			b.buf.Free()
   421  			b.buf = nil
   422  			bufferLog.Debugf("drained buffer")
   423  			n, err = b.readFromStream(p)
   424  		} else {
   425  			bufferLog.Debugf("read %v from buffer", n)
   426  		}
   427  	} else if b.err != nil {
   428  		err = b.err
   429  	} else {
   430  		n, err = b.readFromStream(p)
   431  	}
   432  
   433  	return
   434  }
   435  
   436  func (b *Buffer) Close() (err error) {
   437  	b.mu.Lock()
   438  	defer b.mu.Unlock()
   439  
   440  	if b.reader != nil {
   441  		err = b.reader.Close()
   442  	}
   443  
   444  	if b.buf != nil {
   445  		b.buf.Free()
   446  		b.buf = nil
   447  	}
   448  
   449  	return
   450  }