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 }