github.com/GeniusesGroup/libgo@v0.0.0-20220929090155-5ff932cb408e/timer/timing-wheel.go (about) 1 /* For license and copyright information please see the LEGAL file in the code repository */ 2 3 package timer 4 5 import ( 6 "github.com/GeniusesGroup/libgo/protocol" 7 ) 8 9 // TimingWheel is not concurrent safe and must call each instance by each CPU core separately, 10 // or write upper layer to implement needed logic to prevent data race. 11 type TimingWheel struct { 12 wheelSize int 13 wheel [][]*Async 14 interval protocol.Duration // same as tw.ticker.period 15 pos int 16 ticker Sync 17 stop chan struct{} 18 } 19 20 // if you make one sec interval for a earth day(60*60*24=86400), you need 2MB ram just to hold empty wheel without any timer. 21 func (tw *TimingWheel) Init(interval protocol.Duration, wheelSize int) { 22 tw.wheelSize = wheelSize 23 tw.stop = make(chan struct{}) 24 tw.wheel = make([][]*Async, wheelSize) 25 tw.ticker.Init() 26 tw.interval = interval 27 } 28 29 func (tw *TimingWheel) AddTimer(t *Async) { 30 var addedPosition = tw.addedPosition(t) 31 if addedPosition > tw.wheelSize { 32 panic("timer - wheel: try to add a timer with bad timeout that overflow the current timing wheel") 33 } 34 if addedPosition == tw.pos { 35 t.callback.TimerHandler() 36 tw.checkAndAddTimerAgain(t) 37 return 38 } 39 tw.wheel[addedPosition] = append(tw.wheel[addedPosition], t) 40 } 41 42 // call by go keyword if you don't want the current goroutine block. 43 func (tw *TimingWheel) Start() { 44 tw.ticker.Tick(tw.interval, tw.interval) 45 loop: 46 for { 47 select { 48 case <-tw.ticker.Signal(): 49 var pos = tw.pos 50 tw.incrementTickPosition() 51 var timers = tw.wheel[pos] 52 tw.wheel[pos] = timers[:0] 53 for i := 0; i < len(timers); i++ { 54 var timer = timers[i] 55 timer.callback.TimerHandler() 56 tw.checkAndAddTimerAgain(timer) 57 } 58 case <-tw.stop: 59 close(tw.stop) 60 tw.stop = nil 61 break loop 62 } 63 } 64 tw.ticker.Stop() 65 } 66 67 // Not concurrent safe. 68 func (tw *TimingWheel) Stop() (alreadyStopped bool) { 69 if tw.stop == nil { 70 return true 71 } 72 73 select { 74 case tw.stop <- struct{}{}: 75 default: 76 alreadyStopped = true 77 } 78 tw.ticker.Stop() 79 return 80 } 81 82 func (tw *TimingWheel) incrementTickPosition() { 83 var wheelLen = len(tw.wheel) 84 if wheelLen-1 == tw.pos { 85 tw.pos = 0 86 } else { 87 tw.pos++ 88 } 89 } 90 91 func (tw *TimingWheel) checkAndAddTimerAgain(t *Async) { 92 if t.period == 0 { 93 t.Reinit() 94 } else { 95 var addedPosition = tw.addedPosition(t) 96 tw.wheel[addedPosition] = append(tw.wheel[addedPosition], t) 97 } 98 } 99 100 func (tw *TimingWheel) addedPosition(t *Async) int { 101 return int(t.period/tw.interval) + tw.pos 102 }