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  }