161 lines
3.5 KiB
Go
161 lines
3.5 KiB
Go
|
|
/*
|
|||
|
|
* Copyright (C) THL A29 Limited, a Tencent company. All rights reserved.
|
|||
|
|
*
|
|||
|
|
* SPDX-License-Identifier: Apache-2.0
|
|||
|
|
*
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
package lockfree
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"runtime"
|
|||
|
|
"sync"
|
|||
|
|
"sync/atomic"
|
|||
|
|
"time"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// blockStrategy 阻塞策略
|
|||
|
|
type blockStrategy interface {
|
|||
|
|
// block 阻塞
|
|||
|
|
block(actual *uint64, expected uint64)
|
|||
|
|
|
|||
|
|
// release 释放阻塞
|
|||
|
|
release()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// SchedBlockStrategy 调度等待策略
|
|||
|
|
// 调用runtime.Gosched()方法使当前 g 主动让出 cpu 资源。
|
|||
|
|
type SchedBlockStrategy struct {
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *SchedBlockStrategy) block(actual *uint64, expected uint64) {
|
|||
|
|
runtime.Gosched()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *SchedBlockStrategy) release() {
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// SleepBlockStrategy 休眠等待策略
|
|||
|
|
// 调用 Sleep 方法使当前 g 主动让出 cpu 资源。
|
|||
|
|
// sleep poll 参考值:
|
|||
|
|
// 轮询时长为 10us 时,cpu 开销约 2-3% 左右。
|
|||
|
|
// 轮询时长为 5us 时,cpu 开销约在 10% 左右。
|
|||
|
|
// 轮询时长小于 5us 时,cpu 开销接近 100% 满载。
|
|||
|
|
type SleepBlockStrategy struct {
|
|||
|
|
t time.Duration
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewSleepBlockStrategy(wait time.Duration) *SleepBlockStrategy {
|
|||
|
|
return &SleepBlockStrategy{
|
|||
|
|
t: wait,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *SleepBlockStrategy) block(actual *uint64, expected uint64) {
|
|||
|
|
time.Sleep(s.t)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *SleepBlockStrategy) release() {
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ProcYieldBlockStrategy CPU空指令策略
|
|||
|
|
type ProcYieldBlockStrategy struct {
|
|||
|
|
cycle uint32
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewProcYieldBlockStrategy(cycle uint32) *ProcYieldBlockStrategy {
|
|||
|
|
return &ProcYieldBlockStrategy{
|
|||
|
|
cycle: cycle,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *ProcYieldBlockStrategy) block(actual *uint64, expected uint64) {
|
|||
|
|
procyield(s.cycle)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *ProcYieldBlockStrategy) release() {
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// OSYieldBlockStrategy 操作系统调度策略
|
|||
|
|
type OSYieldBlockStrategy struct {
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewOSYieldWaitStrategy() *OSYieldBlockStrategy {
|
|||
|
|
return &OSYieldBlockStrategy{}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *OSYieldBlockStrategy) block(actual *uint64, expected uint64) {
|
|||
|
|
osyield()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *OSYieldBlockStrategy) release() {
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ChanBlockStrategy chan阻塞策略
|
|||
|
|
type ChanBlockStrategy struct {
|
|||
|
|
bc chan struct{}
|
|||
|
|
b uint32
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewChanBlockStrategy() *ChanBlockStrategy {
|
|||
|
|
return &ChanBlockStrategy{
|
|||
|
|
bc: make(chan struct{}),
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *ChanBlockStrategy) block(actual *uint64, expected uint64) {
|
|||
|
|
// 0:未阻塞;1:阻塞
|
|||
|
|
if atomic.CompareAndSwapUint32(&s.b, 0, 1) {
|
|||
|
|
// 设置成功的话,表示阻塞,需要进行二次判断
|
|||
|
|
if atomic.LoadUint64(actual) == expected {
|
|||
|
|
// 表示阻塞失败,因为结果是一致的,此处需要重新将状态调整回来
|
|||
|
|
if atomic.CompareAndSwapUint32(&s.b, 1, 0) {
|
|||
|
|
// 表示回调成功,直接退出即可
|
|||
|
|
return
|
|||
|
|
} else {
|
|||
|
|
// 表示有其他协程release了,则读取对应chan即可
|
|||
|
|
<-s.bc
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 如果说结果不一致,则表示阻塞,等待被释放即可
|
|||
|
|
<-s.bc
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 没有设置成功,不用关注
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *ChanBlockStrategy) release() {
|
|||
|
|
if atomic.CompareAndSwapUint32(&s.b, 1, 0) {
|
|||
|
|
// 表示可以释放,即chan是等待状态
|
|||
|
|
s.bc <- struct{}{}
|
|||
|
|
}
|
|||
|
|
// 无法设置则不用关心
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ConditionBlockStrategy condition 阻塞策略
|
|||
|
|
type ConditionBlockStrategy struct {
|
|||
|
|
cond *sync.Cond
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewConditionBlockStrategy() *ConditionBlockStrategy {
|
|||
|
|
return &ConditionBlockStrategy{
|
|||
|
|
cond: sync.NewCond(&sync.Mutex{}),
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *ConditionBlockStrategy) block(actual *uint64, expected uint64) {
|
|||
|
|
s.cond.L.Lock()
|
|||
|
|
defer s.cond.L.Unlock()
|
|||
|
|
if atomic.LoadUint64(actual) == expected {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
s.cond.Wait()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *ConditionBlockStrategy) release() {
|
|||
|
|
s.cond.L.Lock()
|
|||
|
|
defer s.cond.L.Unlock()
|
|||
|
|
s.cond.Broadcast()
|
|||
|
|
}
|