github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/lib/ipc/x_block_strategies.go (about) 1 package ipc 2 3 import ( 4 "runtime" 5 "sync" 6 "sync/atomic" 7 "time" 8 "unsafe" 9 10 "github.com/benz9527/xboot/lib/infra" 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 func NewXCpuNoOpLoopBlockStrategy(cycles uint32) BlockStrategy { 64 return &xCpuNoOpLoopBlockStrategy{ 65 cycles: cycles, 66 } 67 } 68 69 func (bs *xCpuNoOpLoopBlockStrategy) WaitFor(eqFn func() bool) { 70 infra.ProcYield(bs.cycles) 71 } 72 73 func (bs *xCpuNoOpLoopBlockStrategy) Done() {} 74 75 type xOsYieldBlockStrategy struct{} 76 77 func NewXOsYieldBlockStrategy() BlockStrategy { 78 return &xOsYieldBlockStrategy{} 79 } 80 81 func (bs *xOsYieldBlockStrategy) WaitFor(fn func() bool) { 82 infra.OsYield() 83 } 84 85 func (bs *xOsYieldBlockStrategy) Done() {} 86 87 type xCacheChannelBlockStrategy struct { 88 _ [cacheLinePadSize - unsafe.Sizeof(*new(uint64))]byte 89 status uint64 90 _ [cacheLinePadSize - unsafe.Sizeof(*new(uint64))]byte 91 ch chan struct{} 92 } 93 94 func NewXCacheChannelBlockStrategy() BlockStrategy { 95 return &xCacheChannelBlockStrategy{ 96 ch: make(chan struct{}, 1), 97 status: 0, 98 } 99 } 100 101 func (bs *xCacheChannelBlockStrategy) WaitFor(eqFn func() bool) { 102 // Try to block 103 if atomic.CompareAndSwapUint64(&bs.status, 0, 1) { 104 // Double check 105 if !eqFn() { 106 // Block, wait for signal 107 <-bs.ch 108 } else { 109 // Double check failed, reset status 110 if !atomic.CompareAndSwapUint64(&bs.status, 1, 0) { 111 // Wait for release 112 <-bs.ch 113 } 114 } 115 } 116 } 117 118 func (bs *xCacheChannelBlockStrategy) Done() { 119 // Release 120 if atomic.CompareAndSwapUint64(&bs.status, 1, 0) { 121 // Send signal 122 bs.ch <- struct{}{} 123 } 124 } 125 126 // https://gfw.go101.org/article/concurrent-synchronization-more.html 127 128 type xCondBlockStrategy struct { 129 cond *sync.Cond 130 } 131 132 func NewXCondBlockStrategy() BlockStrategy { 133 return &xCondBlockStrategy{ 134 cond: sync.NewCond(&sync.Mutex{}), 135 } 136 } 137 138 func (bs *xCondBlockStrategy) WaitFor(eqFn func() bool) { 139 bs.cond.L.Lock() 140 defer bs.cond.L.Unlock() 141 if eqFn() { 142 return 143 } 144 bs.cond.Wait() 145 } 146 147 func (bs *xCondBlockStrategy) Done() { 148 bs.cond.Broadcast() 149 }