github.com/glide-im/glide@v1.6.0/pkg/timingwheel/timingwheel.go (about)

     1  package timingwheel
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"sync"
     7  	"time"
     8  )
     9  
    10  var Executor = func(f func()) {
    11  	go f()
    12  }
    13  
    14  type Task struct {
    15  	offset int
    16  	s      *slot
    17  	at     time.Time
    18  
    19  	fn func()
    20  	C  chan struct{}
    21  }
    22  
    23  func (s *Task) TTL() int64 {
    24  	now := float64(time.Now().UnixNano())
    25  	at := float64(s.at.UnixNano())
    26  	return int64(math.Floor((at-now)/float64(time.Millisecond) + 1.0/2.0))
    27  }
    28  
    29  func (s *Task) call() {
    30  	if s.s == nil {
    31  		return
    32  	}
    33  	Executor(func() {
    34  		s.Cancel()
    35  		if s.fn != nil {
    36  			s.fn()
    37  		}
    38  		select {
    39  		case s.C <- struct{}{}:
    40  		default:
    41  		}
    42  	})
    43  }
    44  
    45  func (s *Task) Callback(f func()) {
    46  	s.fn = f
    47  }
    48  
    49  func (s *Task) Cancel() {
    50  	if s.s != nil {
    51  		s.s.remove(s)
    52  		s.s = nil
    53  	}
    54  }
    55  
    56  type slot struct {
    57  	index  int
    58  	next   *slot
    59  	len    int
    60  	values map[*Task]interface{}
    61  
    62  	m         sync.Mutex
    63  	circulate bool
    64  }
    65  
    66  func newSlot(circulate bool, len int) *slot {
    67  	var head *slot
    68  	var s *slot
    69  	for i := 0; i < len; i++ {
    70  		n := &slot{
    71  			index:     i,
    72  			len:       len,
    73  			values:    map[*Task]interface{}{},
    74  			circulate: circulate,
    75  			m:         sync.Mutex{},
    76  		}
    77  		if i == 0 {
    78  			head = n
    79  		} else {
    80  			s.next = n
    81  		}
    82  		s = n
    83  	}
    84  	s.next = head
    85  	return s
    86  }
    87  
    88  func (s *slot) put(offset int, v *Task) int {
    89  	if offset < 0 {
    90  		panic("offset less the zero")
    91  	}
    92  	if !s.circulate && s.index == s.len && offset > 0 {
    93  		return offset
    94  	}
    95  	if offset == 0 {
    96  		s.m.Lock()
    97  		s.values[v] = nil
    98  		v.s = s
    99  		s.m.Unlock()
   100  		return 0
   101  	}
   102  	if offset >= s.len {
   103  		return offset - s.len
   104  	}
   105  	return s.next.put(offset-1, v)
   106  }
   107  
   108  func (s *slot) isEmpty() bool {
   109  	s.m.Lock()
   110  	defer s.m.Unlock()
   111  	return len(s.values) == 0
   112  }
   113  
   114  func (s *slot) callAndRm() {
   115  	if s.isEmpty() {
   116  		return
   117  	}
   118  	s.m.Lock()
   119  	for k := range s.values {
   120  		k.call()
   121  	}
   122  	s.m.Unlock()
   123  }
   124  
   125  func (s *slot) remove(v *Task) {
   126  	s.m.Lock()
   127  	delete(s.values, v)
   128  	s.m.Unlock()
   129  }
   130  
   131  func (s *slot) valueArray() []*Task {
   132  	var r []*Task
   133  	s.m.Lock()
   134  	for k := range s.values {
   135  		r = append(r, k)
   136  	}
   137  	s.m.Unlock()
   138  	return r
   139  }
   140  
   141  type wheel struct {
   142  	slotCap int
   143  	remain  int
   144  
   145  	slot *slot
   146  
   147  	parent *wheel
   148  	child  *wheel
   149  }
   150  
   151  func newWheel(buckets int, dep int, wheels int, child *wheel) *wheel {
   152  	wh := &wheel{
   153  		slot:    newSlot(dep == wheels, buckets),
   154  		slotCap: int(math.Pow(float64(buckets), float64(dep))) / buckets,
   155  		child:   child,
   156  	}
   157  	if dep == wheels {
   158  		wh.remain = wh.slotCap * buckets
   159  	}
   160  	if child != nil {
   161  		child.parent = wh
   162  	}
   163  	return wh
   164  }
   165  
   166  func (w *wheel) tick() {
   167  	if w.parent != nil {
   168  		w.remain--
   169  		if w.remain <= 0 {
   170  			w.remain = w.slotCap * w.slot.len
   171  		}
   172  		w.parent.tick()
   173  	}
   174  }
   175  
   176  func (w *wheel) move() bool {
   177  	if w.child != nil {
   178  		for _, v := range w.slot.valueArray() {
   179  			w.slot.remove(v)
   180  			w.child.put(v)
   181  		}
   182  		if w.child.move() {
   183  			w.slot = w.slot.next
   184  			for _, v := range w.slot.valueArray() {
   185  				w.slot.remove(v)
   186  				w.child.put(v)
   187  			}
   188  			return w.slot.index == 0
   189  		} else {
   190  			return false
   191  		}
   192  	} else {
   193  		w.tick()
   194  		w.slot = w.slot.next
   195  		w.slot.callAndRm()
   196  		return w.slot.index == 0
   197  	}
   198  }
   199  
   200  func (w *wheel) put(v *Task) {
   201  
   202  	s := int(math.Floor(float64(v.offset) / float64(w.slotCap)))
   203  	if s == 0 {
   204  		if w.child == nil {
   205  			v.call()
   206  		} else {
   207  			w.child.put(v)
   208  		}
   209  	} else {
   210  		if w.child != nil {
   211  			v.offset = v.offset - ((s-1)*w.slotCap + w.child.remain - 1) - 1
   212  		}
   213  		w.slot.put(s, v)
   214  	}
   215  }
   216  
   217  func (w *wheel) put2(v *Task) {
   218  
   219  	s := int(math.Floor(float64(v.offset) / float64(w.slotCap)))
   220  	sl := w.slot
   221  	if s == 0 {
   222  		if w.child != nil {
   223  			if w.child.remain > v.offset {
   224  				w.child.put2(v)
   225  			} else {
   226  				v.offset = v.offset - w.child.remain
   227  				sl.put(1, v)
   228  			}
   229  		} else {
   230  			sl.put(s, v)
   231  			v.call()
   232  		}
   233  	} else {
   234  		if w.child != nil {
   235  			v.offset = v.offset - ((s-1)*w.slotCap + w.child.remain - 1) - 1
   236  			if v.offset >= w.slotCap {
   237  				s++
   238  				v.offset = v.offset - w.slotCap
   239  			}
   240  		}
   241  		sl.put(s, v)
   242  	}
   243  }
   244  
   245  // TimingWheel the timing wheel ticker implementation
   246  type TimingWheel struct {
   247  	interval   time.Duration
   248  	ticker     *time.Ticker
   249  	quit       chan struct{}
   250  	maxTimeout time.Duration
   251  
   252  	wheel *wheel
   253  }
   254  
   255  func NewTimingWheel(interval time.Duration, wheels int, slots int) *TimingWheel {
   256  	tw := new(TimingWheel)
   257  
   258  	tw.interval = interval
   259  	tw.quit = make(chan struct{})
   260  	s := int64(math.Pow(float64(wheels), float64(slots)))
   261  
   262  	tw.maxTimeout = interval * time.Duration(s)
   263  	tw.ticker = time.NewTicker(interval)
   264  
   265  	var w *wheel
   266  	for i := 1; i <= wheels; i++ {
   267  		wh := newWheel(slots, i, wheels, nil)
   268  		if w != nil {
   269  			wh.child = w
   270  			w.parent = wh
   271  		}
   272  		w = wh
   273  	}
   274  	tw.wheel = w
   275  
   276  	go tw.run()
   277  
   278  	return tw
   279  }
   280  
   281  func (w *TimingWheel) Stop() {
   282  	close(w.quit)
   283  }
   284  
   285  func (w *TimingWheel) After(timeout time.Duration) *Task {
   286  	if timeout >= w.maxTimeout {
   287  		panic(fmt.Sprintf("maxTimeout=%d, current=%d", w.maxTimeout, timeout))
   288  	}
   289  	//offset := int(float64(TTL) / float64(w.interval))
   290  	offset := int(math.Floor(float64(timeout.Milliseconds())/float64(w.interval.Milliseconds()) + 1.0/2.0))
   291  
   292  	ch := make(chan struct{})
   293  
   294  	t := &Task{
   295  		offset: offset,
   296  		C:      ch,
   297  		at:     time.Now().Add(timeout),
   298  	}
   299  	w.wheel.put2(t)
   300  	return t
   301  }
   302  
   303  func (w *TimingWheel) run() {
   304  	for {
   305  		select {
   306  		case <-w.ticker.C:
   307  			w.onTicker()
   308  		case <-w.quit:
   309  			w.ticker.Stop()
   310  			return
   311  		}
   312  	}
   313  }
   314  
   315  func (w *TimingWheel) onTicker() {
   316  	w.wheel.move()
   317  }