115 lines
2.7 KiB
Go
115 lines
2.7 KiB
Go
// Copyright 2020-2024 guonaihong, antlabs. All rights reserved.
|
||
//
|
||
// mit license
|
||
package timer
|
||
|
||
import (
|
||
"sync"
|
||
"sync/atomic"
|
||
"time"
|
||
"unsafe"
|
||
|
||
"github.com/antlabs/stl/list"
|
||
)
|
||
|
||
const (
|
||
haveStop = uint32(1)
|
||
)
|
||
|
||
// 先使用sync.Mutex实现功能
|
||
// 后面使用cas优化
|
||
type Time struct {
|
||
timeNode
|
||
sync.Mutex
|
||
|
||
// |---16bit---|---16bit---|------32bit-----|
|
||
// |---level---|---index---|-------seq------|
|
||
// level 在near盘子里就是1, 在T2ToTt[0]盘子里就是2起步
|
||
// index 就是各自盘子的索引值
|
||
// seq 自增id
|
||
version atomic.Uint64
|
||
}
|
||
|
||
func newTimeHead(level uint64, index uint64) *Time {
|
||
head := &Time{}
|
||
head.version.Store(genVersionHeight(level, index))
|
||
head.Init()
|
||
return head
|
||
}
|
||
|
||
func genVersionHeight(level uint64, index uint64) uint64 {
|
||
return level<<(32+16) | index<<32
|
||
}
|
||
|
||
func (t *Time) lockPushBack(node *timeNode, level uint64, index uint64) {
|
||
t.Lock()
|
||
defer t.Unlock()
|
||
if node.stop.Load() == haveStop {
|
||
return
|
||
}
|
||
|
||
t.AddTail(&node.Head)
|
||
atomic.StorePointer(&node.list, unsafe.Pointer(t))
|
||
//更新节点的version信息
|
||
node.version.Store(t.version.Load())
|
||
}
|
||
|
||
type timeNode struct {
|
||
expire uint64
|
||
userExpire time.Duration
|
||
callback func()
|
||
stop atomic.Uint32
|
||
list unsafe.Pointer //存放表头信息
|
||
version atomic.Uint64 //保存节点版本信息
|
||
isSchedule bool
|
||
root *timeWheel
|
||
list.Head
|
||
}
|
||
|
||
// 一个timeNode节点有4个状态
|
||
// 1.存在于初始化链表中
|
||
// 2.被移动到tmp链表
|
||
// 3.1 和 3.2是if else的状态
|
||
//
|
||
// 3.1被移动到new链表
|
||
// 3.2直接执行
|
||
//
|
||
// 1和3.1状态是没有问题的
|
||
// 2和3.2状态会是没有锁保护下的操作,会有数据竞争
|
||
func (t *timeNode) Stop() bool {
|
||
|
||
t.stop.Store(haveStop)
|
||
|
||
// 使用版本号算法让timeNode知道自己是否被移动了
|
||
// timeNode的version和表头的version一样表示没有被移动可以直接删除
|
||
// 如果不一样,可能在第2或者3.2状态,使用惰性删除
|
||
cpyList := (*Time)(atomic.LoadPointer(&t.list))
|
||
cpyList.Lock()
|
||
defer cpyList.Unlock()
|
||
if t.version.Load() != cpyList.version.Load() {
|
||
return false
|
||
}
|
||
|
||
cpyList.Del(&t.Head)
|
||
return true
|
||
}
|
||
|
||
// warning: 该函数目前没有稳定
|
||
func (t *timeNode) Reset(expire time.Duration) bool {
|
||
cpyList := (*Time)(atomic.LoadPointer(&t.list))
|
||
cpyList.Lock()
|
||
defer cpyList.Unlock()
|
||
// TODO: 这里有一个问题,如果在执行Reset的时候,这个节点已经被移动到tmp链表
|
||
// if atomic.LoadUint64(&t.version) != atomic.LoadUint64(&cpyList.version) {
|
||
// return
|
||
// }
|
||
cpyList.Del(&t.Head)
|
||
jiffies := atomic.LoadUint64(&t.root.jiffies)
|
||
|
||
expire = expire/(time.Millisecond*10) + time.Duration(jiffies)
|
||
t.expire = uint64(expire)
|
||
|
||
t.root.add(t, jiffies)
|
||
return true
|
||
}
|