github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/syndtr/goleveldb/leveldb/util/buffer_pool.go (about)

     1  // Copyright (c) 2014, Suryandaru Triandana <syndtr@gmail.com>
     2  // All rights reserved.
     3  //
     4  // Use of this source code is governed by a BSD-style license that can be
     5  // found in the LICENSE file.
     6  
     7  package util
     8  
     9  import (
    10  	"fmt"
    11  	"sync"
    12  	"sync/atomic"
    13  	"time"
    14  )
    15  
    16  type buffer struct {
    17  	b    []byte
    18  	miss int
    19  }
    20  
    21  // BufferPool is a 'buffer pool'.
    22  type BufferPool struct {
    23  	pool      [6]chan []byte
    24  	size      [5]uint32
    25  	sizeMiss  [5]uint32
    26  	sizeHalf  [5]uint32
    27  	baseline  [4]int
    28  	baseline0 int
    29  
    30  	mu     sync.RWMutex
    31  	closed bool
    32  	closeC chan struct{}
    33  
    34  	get     uint32
    35  	put     uint32
    36  	half    uint32
    37  	less    uint32
    38  	equal   uint32
    39  	greater uint32
    40  	miss    uint32
    41  }
    42  
    43  func (p *BufferPool) poolNum(n int) int {
    44  	if n <= p.baseline0 && n > p.baseline0/2 {
    45  		return 0
    46  	}
    47  	for i, x := range p.baseline {
    48  		if n <= x {
    49  			return i + 1
    50  		}
    51  	}
    52  	return len(p.baseline) + 1
    53  }
    54  
    55  // Get returns buffer with length of n.
    56  func (p *BufferPool) Get(n int) []byte {
    57  	if p == nil {
    58  		return make([]byte, n)
    59  	}
    60  
    61  	p.mu.RLock()
    62  	defer p.mu.RUnlock()
    63  
    64  	if p.closed {
    65  		return make([]byte, n)
    66  	}
    67  
    68  	atomic.AddUint32(&p.get, 1)
    69  
    70  	poolNum := p.poolNum(n)
    71  	pool := p.pool[poolNum]
    72  	if poolNum == 0 {
    73  		// Fast path.
    74  		select {
    75  		case b := <-pool:
    76  			switch {
    77  			case cap(b) > n:
    78  				if cap(b)-n >= n {
    79  					atomic.AddUint32(&p.half, 1)
    80  					select {
    81  					case pool <- b:
    82  					default:
    83  					}
    84  					return make([]byte, n)
    85  				} else {
    86  					atomic.AddUint32(&p.less, 1)
    87  					return b[:n]
    88  				}
    89  			case cap(b) == n:
    90  				atomic.AddUint32(&p.equal, 1)
    91  				return b[:n]
    92  			default:
    93  				atomic.AddUint32(&p.greater, 1)
    94  			}
    95  		default:
    96  			atomic.AddUint32(&p.miss, 1)
    97  		}
    98  
    99  		return make([]byte, n, p.baseline0)
   100  	} else {
   101  		sizePtr := &p.size[poolNum-1]
   102  
   103  		select {
   104  		case b := <-pool:
   105  			switch {
   106  			case cap(b) > n:
   107  				if cap(b)-n >= n {
   108  					atomic.AddUint32(&p.half, 1)
   109  					sizeHalfPtr := &p.sizeHalf[poolNum-1]
   110  					if atomic.AddUint32(sizeHalfPtr, 1) == 20 {
   111  						atomic.StoreUint32(sizePtr, uint32(cap(b)/2))
   112  						atomic.StoreUint32(sizeHalfPtr, 0)
   113  					} else {
   114  						select {
   115  						case pool <- b:
   116  						default:
   117  						}
   118  					}
   119  					return make([]byte, n)
   120  				} else {
   121  					atomic.AddUint32(&p.less, 1)
   122  					return b[:n]
   123  				}
   124  			case cap(b) == n:
   125  				atomic.AddUint32(&p.equal, 1)
   126  				return b[:n]
   127  			default:
   128  				atomic.AddUint32(&p.greater, 1)
   129  				if uint32(cap(b)) >= atomic.LoadUint32(sizePtr) {
   130  					select {
   131  					case pool <- b:
   132  					default:
   133  					}
   134  				}
   135  			}
   136  		default:
   137  			atomic.AddUint32(&p.miss, 1)
   138  		}
   139  
   140  		if size := atomic.LoadUint32(sizePtr); uint32(n) > size {
   141  			if size == 0 {
   142  				atomic.CompareAndSwapUint32(sizePtr, 0, uint32(n))
   143  			} else {
   144  				sizeMissPtr := &p.sizeMiss[poolNum-1]
   145  				if atomic.AddUint32(sizeMissPtr, 1) == 20 {
   146  					atomic.StoreUint32(sizePtr, uint32(n))
   147  					atomic.StoreUint32(sizeMissPtr, 0)
   148  				}
   149  			}
   150  			return make([]byte, n)
   151  		} else {
   152  			return make([]byte, n, size)
   153  		}
   154  	}
   155  }
   156  
   157  // Put adds given buffer to the pool.
   158  func (p *BufferPool) Put(b []byte) {
   159  	if p == nil {
   160  		return
   161  	}
   162  
   163  	p.mu.RLock()
   164  	defer p.mu.RUnlock()
   165  
   166  	if p.closed {
   167  		return
   168  	}
   169  
   170  	atomic.AddUint32(&p.put, 1)
   171  
   172  	pool := p.pool[p.poolNum(cap(b))]
   173  	select {
   174  	case pool <- b:
   175  	default:
   176  	}
   177  
   178  }
   179  
   180  func (p *BufferPool) Close() {
   181  	if p == nil {
   182  		return
   183  	}
   184  
   185  	p.mu.Lock()
   186  	if !p.closed {
   187  		p.closed = true
   188  		p.closeC <- struct{}{}
   189  	}
   190  	p.mu.Unlock()
   191  }
   192  
   193  func (p *BufferPool) String() string {
   194  	if p == nil {
   195  		return "<nil>"
   196  	}
   197  
   198  	return fmt.Sprintf("BufferPool{B·%d Z·%v Zm·%v Zh·%v G·%d P·%d H·%d <·%d =·%d >·%d M·%d}",
   199  		p.baseline0, p.size, p.sizeMiss, p.sizeHalf, p.get, p.put, p.half, p.less, p.equal, p.greater, p.miss)
   200  }
   201  
   202  func (p *BufferPool) drain() {
   203  	ticker := time.NewTicker(2 * time.Second)
   204  	defer ticker.Stop()
   205  	for {
   206  		select {
   207  		case <-ticker.C:
   208  			for _, ch := range p.pool {
   209  				select {
   210  				case <-ch:
   211  				default:
   212  				}
   213  			}
   214  		case <-p.closeC:
   215  			close(p.closeC)
   216  			for _, ch := range p.pool {
   217  				close(ch)
   218  			}
   219  			return
   220  		}
   221  	}
   222  }
   223  
   224  // NewBufferPool creates a new initialized 'buffer pool'.
   225  func NewBufferPool(baseline int) *BufferPool {
   226  	if baseline <= 0 {
   227  		panic("baseline can't be <= 0")
   228  	}
   229  	p := &BufferPool{
   230  		baseline0: baseline,
   231  		baseline:  [...]int{baseline / 4, baseline / 2, baseline * 2, baseline * 4},
   232  		closeC:    make(chan struct{}, 1),
   233  	}
   234  	for i, cap := range []int{2, 2, 4, 4, 2, 1} {
   235  		p.pool[i] = make(chan []byte, cap)
   236  	}
   237  	go p.drain()
   238  	return p
   239  }