github.com/jxskiss/gopkg@v0.17.3/lru/walbuf.go (about)

     1  package lru
     2  
     3  import "sync"
     4  
     5  const (
     6  	// walBufSize must be power of two
     7  	walBufSize = 1024
     8  	walSetSize = walBufSize * 2
     9  	walSetMask = walSetSize - 1
    10  )
    11  
    12  var walbufpool sync.Pool
    13  
    14  func newWalBuf() *walbuf {
    15  	if buf := walbufpool.Get(); buf != nil {
    16  		return buf.(*walbuf)
    17  	}
    18  	return &walbuf{}
    19  }
    20  
    21  // 关于 promotion 和 walbuf 的并发安全性
    22  //
    23  // 1. Cache.buf 永远不为 nil, 当 Cache.buf 写满时,promote 方法中创建新的 walbuf
    24  //    并使用 CAS 操作赋值给 Cache.buf, 成功执行 CAS 的 goroutine 负责触发 flushBuf;
    25  // 2. Cache.promote 方法中对 buf.p 原子加一,每个 goroutine 写入自己拿到的索引位置,
    26  //    不同 goroutine 不会同时写入同一个内存位置;
    27  // 3. 当 Cache.promote 方法被调用时,调用者(Get相关方法)一定持有了 RLock,
    28  //    在 flush walbuf 时,会持有排他锁,因此 promote 方法和 flushBuf 方法一定不会
    29  //    同时执行,flushBuf 函数可以排他地读写 walbuf 的数据;
    30  // 4. flushBuf 方法接受的 walbuf 参数是从 Cache.buf 中 CAS 出来的,又因为 promote
    31  //    和 flushBuf 方法的互斥性,因此保证了一个 walbuf 被传递给 flushBuf 方法后,
    32  //    不会被其他任何 goroutine 持有,flushBuf 结束后,可以安全放回 walbufpool 重用;
    33  
    34  // walbuf helps to reduce lock-contention of read requests from the cache.
    35  type walbuf struct {
    36  	b [walBufSize]uint32
    37  	s [walSetSize]uint32
    38  	p int32
    39  }
    40  
    41  func (wbuf *walbuf) reset() {
    42  	wbuf.p = 0
    43  	for i := range wbuf.s { // memclr
    44  		wbuf.s[i] = 0
    45  	}
    46  }
    47  
    48  func (wbuf *walbuf) deduplicate() []uint32 {
    49  	// Note that we have already checked wbuf.p > 0.
    50  	ln := wbuf.p
    51  	if ln > walBufSize {
    52  		ln = walBufSize
    53  	}
    54  
    55  	set := fastHashset(wbuf.s)
    56  	b, p := wbuf.b[:], ln-1
    57  	for i := ln - 1; i >= 0; i-- {
    58  		idx := b[i]
    59  		if !set.has(idx) {
    60  			set.add(idx)
    61  			b[p] = idx
    62  			p--
    63  		}
    64  	}
    65  	return b[p+1 : ln]
    66  }
    67  
    68  type fastHashset [walSetSize]uint32
    69  
    70  // intPhi is for scrambling the values
    71  const intPhi = 0x9E3779B9
    72  
    73  func phiMix(x int64) int64 {
    74  	h := x * intPhi
    75  	return h ^ (h >> 16)
    76  }
    77  
    78  func (s *fastHashset) add(value uint32) {
    79  	value += 1
    80  
    81  	// Manually inline function phiMix.
    82  	h := int64(value) * intPhi
    83  	ptr := h ^ (h >> 16)
    84  
    85  	for {
    86  		ptr &= walSetMask
    87  		k := s[ptr]
    88  		if k == 0 {
    89  			s[ptr] = value
    90  			return
    91  		}
    92  		if k == value {
    93  			return
    94  		}
    95  		ptr += 1
    96  	}
    97  }
    98  
    99  func (s *fastHashset) has(value uint32) bool {
   100  	value += 1
   101  
   102  	// Manually inline function phiMix.
   103  	h := int64(value) * intPhi
   104  	ptr := h ^ (h >> 16)
   105  
   106  	for {
   107  		ptr &= walSetMask
   108  		k := s[ptr]
   109  		if k == value {
   110  			return true
   111  		}
   112  		if k == 0 {
   113  			return false
   114  		}
   115  		ptr += 1
   116  	}
   117  }