github.com/geniusesgroup/libgo@v0.0.0-20220713101832-828057a9d3d4/timer/timing-wheel.go (about)

     1  /* For license and copyright information please see LEGAL file in repository */
     2  
     3  package timer
     4  
     5  import (
     6  	"../protocol"
     7  )
     8  
     9  type TimingWheel struct {
    10  	wheel    [][]*Timer
    11  	interval protocol.Duration // same as tw.ticker.period
    12  	pos      int
    13  	ticker   Timer
    14  	stop     chan struct{}
    15  }
    16  
    17  // 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.
    18  func (tw *TimingWheel) Init(interval protocol.Duration, wheelSize int) {
    19  	tw.stop = make(chan struct{})
    20  	tw.wheel = make([][]*Timer, wheelSize)
    21  	tw.ticker.Init(nil, nil)
    22  	tw.interval = interval
    23  }
    24  
    25  func (tw *TimingWheel) AddTimer(t *Timer) {
    26  	var addedPosition = tw.addedPosition(t)
    27  	if addedPosition == tw.pos {
    28  		// TODO::: run the timer??
    29  		return
    30  	}
    31  	tw.wheel[addedPosition] = append(tw.wheel[addedPosition], t)
    32  }
    33  
    34  // call by go keyword if you don't want the current goroutine block.
    35  func (tw *TimingWheel) Start() {
    36  	tw.ticker.Tick(tw.interval, tw.interval, -1)
    37  LOOP:
    38  	for {
    39  		select {
    40  		case <-tw.ticker.Signal():
    41  			var pos = tw.pos
    42  			tw.incrementTickPosition()
    43  			var timers = tw.wheel[pos]
    44  			tw.wheel[pos] = timers[:0]
    45  			for i := 0; i < len(timers); i++ {
    46  				var timer = timers[i]
    47  				timer.callback(timer.arg)
    48  				tw.checkAndAddTimerAgain(timer)
    49  			}
    50  		case <-tw.stop:
    51  			tw.stop = nil
    52  			break LOOP
    53  		}
    54  	}
    55  	tw.ticker.Stop()
    56  }
    57  
    58  // Not concurrent safe.
    59  func (tw *TimingWheel) Stop() (alreadyStopped bool) {
    60  	if tw.stop == nil {
    61  		return true
    62  	}
    63  
    64  	select {
    65  	case tw.stop <- struct{}{}:
    66  	default:
    67  		alreadyStopped = true
    68  	}
    69  	tw.ticker.Stop()
    70  	return
    71  }
    72  
    73  func (tw *TimingWheel) incrementTickPosition() {
    74  	var wheelLen = len(tw.wheel)
    75  	if wheelLen-1 == tw.pos {
    76  		tw.pos = 0
    77  	} else {
    78  		tw.pos++
    79  	}
    80  }
    81  
    82  func (tw *TimingWheel) checkAndAddTimerAgain(t *Timer) {
    83  	if t.periodNumber == 0 {
    84  		t.reset()
    85  	} else {
    86  		var addedPosition = tw.addedPosition(t)
    87  		tw.wheel[addedPosition] = append(tw.wheel[addedPosition], t)
    88  		if t.periodNumber > 0 {
    89  			t.periodNumber--
    90  		}
    91  	}
    92  }
    93  
    94  func (tw *TimingWheel) addedPosition(t *Timer) int {
    95  	return int(t.period/tw.interval) + tw.pos
    96  }