github.com/bruceshao/lockfree@v1.1.3-0.20230816090528-e89824c0a6e9/blocks.go (about)

     1  /*
     2   * Copyright (C) THL A29 Limited, a Tencent company. All rights reserved.
     3   *
     4   * SPDX-License-Identifier: Apache-2.0
     5   *
     6   */
     7  
     8  package lockfree
     9  
    10  import (
    11  	"runtime"
    12  	"sync"
    13  	"sync/atomic"
    14  	"time"
    15  )
    16  
    17  // blockStrategy 阻塞策略
    18  type blockStrategy interface {
    19  	// block 阻塞
    20  	block(actual *uint64, expected uint64)
    21  
    22  	// release 释放阻塞
    23  	release()
    24  }
    25  
    26  // SchedBlockStrategy 调度等待策略
    27  // 调用runtime.Gosched()方法使当前 g 主动让出 cpu 资源。
    28  type SchedBlockStrategy struct {
    29  }
    30  
    31  func (s *SchedBlockStrategy) block(actual *uint64, expected uint64) {
    32  	runtime.Gosched()
    33  }
    34  
    35  func (s *SchedBlockStrategy) release() {
    36  }
    37  
    38  // SleepBlockStrategy 休眠等待策略
    39  // 调用 Sleep 方法使当前 g 主动让出 cpu 资源。
    40  // sleep poll 参考值:
    41  // 轮询时长为 10us 时,cpu 开销约 2-3% 左右。
    42  // 轮询时长为 5us 时,cpu 开销约在 10% 左右。
    43  // 轮询时长小于 5us 时,cpu 开销接近 100% 满载。
    44  type SleepBlockStrategy struct {
    45  	t time.Duration
    46  }
    47  
    48  func NewSleepBlockStrategy(wait time.Duration) *SleepBlockStrategy {
    49  	return &SleepBlockStrategy{
    50  		t: wait,
    51  	}
    52  }
    53  
    54  func (s *SleepBlockStrategy) block(actual *uint64, expected uint64) {
    55  	time.Sleep(s.t)
    56  }
    57  
    58  func (s *SleepBlockStrategy) release() {
    59  }
    60  
    61  // ProcYieldBlockStrategy CPU空指令策略
    62  type ProcYieldBlockStrategy struct {
    63  	cycle uint32
    64  }
    65  
    66  func NewProcYieldBlockStrategy(cycle uint32) *ProcYieldBlockStrategy {
    67  	return &ProcYieldBlockStrategy{
    68  		cycle: cycle,
    69  	}
    70  }
    71  
    72  func (s *ProcYieldBlockStrategy) block(actual *uint64, expected uint64) {
    73  	procyield(s.cycle)
    74  }
    75  
    76  func (s *ProcYieldBlockStrategy) release() {
    77  }
    78  
    79  // OSYieldBlockStrategy 操作系统调度策略
    80  type OSYieldBlockStrategy struct {
    81  }
    82  
    83  func NewOSYieldWaitStrategy() *OSYieldBlockStrategy {
    84  	return &OSYieldBlockStrategy{}
    85  }
    86  
    87  func (s *OSYieldBlockStrategy) block(actual *uint64, expected uint64) {
    88  	osyield()
    89  }
    90  
    91  func (s *OSYieldBlockStrategy) release() {
    92  }
    93  
    94  // ChanBlockStrategy chan阻塞策略
    95  type ChanBlockStrategy struct {
    96  	bc chan struct{}
    97  	b  uint32
    98  }
    99  
   100  func NewChanBlockStrategy() *ChanBlockStrategy {
   101  	return &ChanBlockStrategy{
   102  		bc: make(chan struct{}),
   103  	}
   104  }
   105  
   106  func (s *ChanBlockStrategy) block(actual *uint64, expected uint64) {
   107  	// 0:未阻塞;1:阻塞
   108  	if atomic.CompareAndSwapUint32(&s.b, 0, 1) {
   109  		// 设置成功的话,表示阻塞,需要进行二次判断
   110  		if atomic.LoadUint64(actual) == expected {
   111  			// 表示阻塞失败,因为结果是一致的,此处需要重新将状态调整回来
   112  			if atomic.CompareAndSwapUint32(&s.b, 1, 0) {
   113  				// 表示回调成功,直接退出即可
   114  				return
   115  			} else {
   116  				// 表示有其他协程release了,则读取对应chan即可
   117  				<-s.bc
   118  			}
   119  		} else {
   120  			// 如果说结果不一致,则表示阻塞,等待被释放即可
   121  			<-s.bc
   122  		}
   123  	}
   124  	// 没有设置成功,不用关注
   125  }
   126  
   127  func (s *ChanBlockStrategy) release() {
   128  	if atomic.CompareAndSwapUint32(&s.b, 1, 0) {
   129  		// 表示可以释放,即chan是等待状态
   130  		s.bc <- struct{}{}
   131  	}
   132  	// 无法设置则不用关心
   133  	return
   134  }
   135  
   136  // ConditionBlockStrategy condition 阻塞策略
   137  type ConditionBlockStrategy struct {
   138  	cond *sync.Cond
   139  }
   140  
   141  func NewConditionBlockStrategy() *ConditionBlockStrategy {
   142  	return &ConditionBlockStrategy{
   143  		cond: sync.NewCond(&sync.Mutex{}),
   144  	}
   145  }
   146  
   147  func (s *ConditionBlockStrategy) block(actual *uint64, expected uint64) {
   148  	s.cond.L.Lock()
   149  	defer s.cond.L.Unlock()
   150  	if atomic.LoadUint64(actual) == expected {
   151  		return
   152  	}
   153  	s.cond.Wait()
   154  }
   155  
   156  func (s *ConditionBlockStrategy) release() {
   157  	s.cond.L.Lock()
   158  	defer s.cond.L.Unlock()
   159  	s.cond.Broadcast()
   160  }