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