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 }