github.com/benz9527/toy-box/algo@v0.0.0-20240221120937-66c0c6bd5abd/pubsub/x_block_strategies.go (about) 1 package pubsub 2 3 import ( 4 "github.com/benz9527/toy-box/algo/queue" 5 "runtime" 6 "sync" 7 "sync/atomic" 8 "time" 9 "unsafe" 10 _ "unsafe" 11 ) 12 13 var ( 14 _ BlockStrategy = (*xGoSchedBlockStrategy)(nil) 15 _ BlockStrategy = (*xSleepBlockStrategy)(nil) 16 _ BlockStrategy = (*xCpuNoOpLoopBlockStrategy)(nil) 17 _ BlockStrategy = (*xOsYieldBlockStrategy)(nil) 18 _ BlockStrategy = (*xCacheChannelBlockStrategy)(nil) 19 _ BlockStrategy = (*xCondBlockStrategy)(nil) 20 ) 21 22 type xGoSchedBlockStrategy struct{} 23 24 func NewXGoSchedBlockStrategy() BlockStrategy { 25 return &xGoSchedBlockStrategy{} 26 } 27 28 func (x *xGoSchedBlockStrategy) WaitFor(eqFn func() bool) { 29 // Let go runtime schedule other goroutines 30 // Current goroutine will yield CPU 31 runtime.Gosched() 32 } 33 34 func (x *xGoSchedBlockStrategy) Done() { 35 // do nothing 36 } 37 38 type xSleepBlockStrategy struct { 39 sleepTime time.Duration 40 } 41 42 func NewXSleepBlockStrategy(sleepTime time.Duration) BlockStrategy { 43 // around 10us, cpu load 2-3% 44 // eq to 5us, cpu load 10% 45 // lt 5us, cpu load 100% 46 return &xSleepBlockStrategy{ 47 sleepTime: sleepTime, 48 } 49 } 50 51 func (bs *xSleepBlockStrategy) WaitFor(eqFn func() bool) { 52 time.Sleep(bs.sleepTime) 53 } 54 55 func (bs *xSleepBlockStrategy) Done() { 56 // do nothing 57 } 58 59 type xCpuNoOpLoopBlockStrategy struct { 60 cycles uint32 61 } 62 63 //go:linkname procYield runtime.procyield 64 func procYield(cycles uint32) 65 66 func NewXCpuNoOpLoopBlockStrategy(cycles uint32) BlockStrategy { 67 return &xCpuNoOpLoopBlockStrategy{ 68 cycles: cycles, 69 } 70 } 71 72 func (bs *xCpuNoOpLoopBlockStrategy) WaitFor(eqFn func() bool) { 73 procYield(bs.cycles) 74 } 75 76 func (bs *xCpuNoOpLoopBlockStrategy) Done() {} 77 78 //go:linkname osYield runtime.osyield 79 func osYield() 80 81 type xOsYieldBlockStrategy struct{} 82 83 func NewXOsYieldBlockStrategy() BlockStrategy { 84 return &xOsYieldBlockStrategy{} 85 } 86 87 func (bs *xOsYieldBlockStrategy) WaitFor(fn func() bool) { 88 osYield() 89 } 90 91 func (bs *xOsYieldBlockStrategy) Done() {} 92 93 type xCacheChannelBlockStrategy struct { 94 _ [queue.CacheLinePadSize - unsafe.Sizeof(*new(uint64))]byte 95 status uint64 96 _ [queue.CacheLinePadSize - unsafe.Sizeof(*new(uint64))]byte 97 ch chan struct{} 98 } 99 100 func NewXCacheChannelBlockStrategy() BlockStrategy { 101 return &xCacheChannelBlockStrategy{ 102 ch: make(chan struct{}, 1), 103 status: 0, 104 } 105 } 106 107 func (bs *xCacheChannelBlockStrategy) WaitFor(eqFn func() bool) { 108 // Try to block 109 if atomic.CompareAndSwapUint64(&bs.status, 0, 1) { 110 // Double check 111 if !eqFn() { 112 // Block, wait for signal 113 <-bs.ch 114 } else { 115 // Double check failed, reset status 116 if !atomic.CompareAndSwapUint64(&bs.status, 1, 0) { 117 // Wait for release 118 <-bs.ch 119 } 120 } 121 } 122 } 123 124 func (bs *xCacheChannelBlockStrategy) Done() { 125 // Release 126 if atomic.CompareAndSwapUint64(&bs.status, 1, 0) { 127 // Send signal 128 bs.ch <- struct{}{} 129 } 130 } 131 132 // https://gfw.go101.org/article/concurrent-synchronization-more.html 133 134 type xCondBlockStrategy struct { 135 cond *sync.Cond 136 } 137 138 func NewXCondBlockStrategy() BlockStrategy { 139 return &xCondBlockStrategy{ 140 cond: sync.NewCond(&sync.Mutex{}), 141 } 142 } 143 144 func (bs *xCondBlockStrategy) WaitFor(eqFn func() bool) { 145 bs.cond.L.Lock() 146 defer bs.cond.L.Unlock() 147 if eqFn() { 148 return 149 } 150 bs.cond.Wait() 151 } 152 153 func (bs *xCondBlockStrategy) Done() { 154 bs.cond.Broadcast() 155 }