1 /* 2 * Hunt - A refined core library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.event.timer.Common; 13 14 import hunt.event.selector.Selector; 15 import hunt.io.channel.AbstractChannel; 16 import hunt.io.channel.Common; 17 import hunt.logging; 18 import hunt.Exceptions; 19 20 import std.datetime; 21 import std.exception; 22 23 enum CustomTimerMinTimeOut = 50; // in ms 24 enum CustomTimerWheelSize = 500; 25 enum CustomTimer_Next_TimeOut = cast(long)(CustomTimerMinTimeOut * (2.0 / 3.0)); 26 27 alias TickedEventHandler = void delegate(Object sender); 28 29 alias UintObject = BaseTypeObject!uint; 30 31 /** 32 */ 33 interface ITimer { 34 35 /// 36 bool isActive(); 37 38 /// in ms 39 size_t interval(); 40 41 /// ditto 42 ITimer interval(size_t v); 43 44 /// ditto 45 ITimer interval(Duration duration); 46 47 /// 48 ITimer onTick(TickedEventHandler handler); 49 50 /// immediately: true to call first event immediately 51 /// once: true to call timed event only once 52 void start(bool immediately = false, bool once = false); 53 54 void stop(); 55 56 void reset(bool immediately = false, bool once = false); 57 58 void reset(size_t interval); 59 60 void reset(Duration duration); 61 } 62 63 /** 64 Timing Wheel manger Class 65 */ 66 final class TimingWheel { 67 /** 68 constructor 69 Params: 70 wheelSize = the Wheel's element router. 71 */ 72 this(uint wheelSize) { 73 if (wheelSize == 0) 74 wheelSize = 2; 75 _list = new NullWheelTimer[wheelSize]; 76 for (int i = 0; i < wheelSize; ++i) { 77 _list[i] = new NullWheelTimer(); 78 } 79 } 80 81 /** 82 add a Timer into the Wheel 83 Params: 84 tm = the timer. 85 wheel = the wheel. 86 */ 87 pragma(inline) void addNewTimer(WheelTimer tm, size_t wheel = 0) { 88 size_t index; 89 if (wheel > 0) 90 index = nextWheel(wheel); 91 else 92 index = getPrev(); 93 94 NullWheelTimer timer = _list[index]; 95 tm._next = timer._next; 96 tm._prev = timer; 97 if (timer._next) 98 timer._next._prev = tm; 99 timer._next = tm; 100 tm._manger = this; 101 } 102 103 /** 104 The Wheel go forward 105 Params: 106 size = forward's element size; 107 Notes: 108 all forward's element will timeout. 109 */ 110 void prevWheel(uint size = 1) { 111 if (size == 0) 112 return; 113 foreach (i; 0 .. size) { 114 NullWheelTimer timer = doNext(); 115 timer.onTimeOut(); 116 } 117 } 118 119 protected: 120 /// get next wheel times 's Wheel 121 pragma(inline) size_t nextWheel(size_t wheel) { 122 auto next = wheel % _list.length; 123 return (_now + next) % _list.length; 124 } 125 126 /// get the index whitch is farthest with current index. 127 size_t getPrev() { 128 if (_now == 0) 129 return (_list.length - 1); 130 else 131 return (_now - 1); 132 } 133 /// go forward a element,and return the element. 134 pragma(inline) NullWheelTimer doNext() { 135 ++_now; 136 if (_now == _list.length) 137 _now = 0; 138 return _list[_now]; 139 } 140 /// rest a timer. 141 pragma(inline) void rest(WheelTimer tm, size_t next) { 142 remove(tm); 143 addNewTimer(tm, next); 144 } 145 /// remove the timer. 146 pragma(inline) void remove(WheelTimer tm) { 147 tm._prev._next = tm._next; 148 if (tm._next) 149 tm._next._prev = tm._prev; 150 tm._manger = null; 151 tm._next = null; 152 tm._prev = null; 153 } 154 155 private: 156 NullWheelTimer[] _list; 157 size_t _now; 158 } 159 160 /** 161 The timer parent's class. 162 */ 163 abstract class WheelTimer { 164 ~this() @nogc { 165 // stop(); 166 } 167 /** 168 the function will be called when the timer timeout. 169 */ 170 void onTimeOut(); 171 172 /// rest the timer. 173 pragma(inline) final void rest(size_t next = 0) { 174 if (_manger) { 175 _manger.rest(this, next); 176 } 177 } 178 179 /// stop the time, it will remove from Wheel. 180 pragma(inline) final void stop() { 181 if (_manger) { 182 _manger.remove(this); 183 } 184 } 185 186 /// the time is active. 187 pragma(inline, true) final bool isActive() { 188 return _manger !is null; 189 } 190 191 /// get the timer only run once. 192 pragma(inline, true) final @property oneShop() { 193 return _oneShop; 194 } 195 /// set the timer only run once. 196 pragma(inline) final @property oneShop(bool one) { 197 _oneShop = one; 198 } 199 200 private: 201 WheelTimer _next = null; 202 WheelTimer _prev = null; 203 TimingWheel _manger = null; 204 bool _oneShop = false; 205 } 206 207 /// the Header Timer in the wheel. 208 class NullWheelTimer : WheelTimer { 209 override void onTimeOut() { 210 WheelTimer tm = _next; 211 212 while (tm) { 213 // WheelTimer timer = tm._next; 214 if (tm.oneShop()) { 215 tm.stop(); 216 } 217 tm.onTimeOut(); 218 tm = tm._next; 219 } 220 } 221 } 222 223 /** 224 */ 225 struct CustomTimer { 226 void init() { 227 if (_timeWheel is null) 228 _timeWheel = new TimingWheel(CustomTimerWheelSize); 229 _nextTime = (Clock.currStdTime() / 10000) + CustomTimerMinTimeOut; 230 } 231 232 int doWheel() { 233 auto nowTime = (Clock.currStdTime() / 10000); 234 // tracef("nowTime - _nextTime = %d", nowTime - _nextTime); 235 while (nowTime >= _nextTime) { 236 _timeWheel.prevWheel(); 237 _nextTime += CustomTimerMinTimeOut; 238 nowTime = (Clock.currStdTime() / 10000); 239 } 240 nowTime = _nextTime - nowTime; 241 return cast(int) nowTime; 242 } 243 244 TimingWheel timeWheel() { 245 return _timeWheel; 246 } 247 248 private: 249 TimingWheel _timeWheel; 250 long _nextTime; 251 } 252 253 /** 254 */ 255 abstract class TimerChannelBase : AbstractChannel, ITimer { 256 257 protected bool _isActive = false; 258 protected size_t _interval = 1000; 259 260 /// Timer tick handler 261 TickedEventHandler ticked; 262 263 this(Selector loop) { 264 super(loop, ChannelType.Timer); 265 _timeOut = 50; 266 } 267 268 /// 269 @property bool isActive() { 270 return _isActive; 271 } 272 273 /// in ms 274 @property size_t interval() { 275 return _interval; 276 } 277 278 /// ditto 279 @property ITimer interval(size_t v) { 280 _interval = v; 281 return this; 282 } 283 284 /// ditto 285 @property ITimer interval(Duration duration) { 286 _interval = cast(size_t) duration.total!("msecs"); 287 return this; 288 } 289 290 /// The handler will be handled in another thread. 291 ITimer onTick(TickedEventHandler handler) { 292 this.ticked = handler; 293 return this; 294 } 295 296 @property size_t wheelSize() { 297 return _wheelSize; 298 } 299 300 @property size_t time() { 301 return _interval; 302 } 303 304 void start(bool immediately = false, bool once = false) { 305 _inLoop.register(this); 306 _isRegistered = true; 307 _isActive = true; 308 } 309 310 void stop() { 311 if (_isActive) { 312 _isActive = false; 313 close(); 314 } 315 } 316 317 void reset(size_t interval) { 318 this.interval = interval; 319 reset(); 320 } 321 322 void reset(Duration duration) { 323 this.interval = duration; 324 reset(); 325 } 326 327 void reset(bool immediately = false, bool once = false) { 328 if (_isActive) { 329 stop(); 330 start(); 331 } 332 } 333 334 override void close() { 335 onClose(); 336 } 337 338 protected void onTick() { 339 // trace("tick thread id: ", getTid()); 340 if (ticked !is null) 341 ticked(this); 342 } 343 344 protected: 345 uint _wheelSize; 346 uint _circle; 347 size_t _timeOut; 348 } 349 350 alias TimeoutHandler = void delegate(Object sender); 351 352 /** 353 */ 354 class HuntWheelTimer : WheelTimer { 355 this() { 356 // time = Clock.currTime(); 357 } 358 359 // override void onTimeOut() nothrow 360 // { 361 // collectException(trace("\nname is ", name, " \tcutterTime is : ", 362 // Clock.currTime().toSimpleString(), "\t new time is : ", time.toSimpleString())); 363 // } 364 365 override void onTimeOut() { 366 _now++; 367 if (_now >= _circle) { 368 _now = 0; 369 if (timeout !is null) { 370 timeout(this); 371 } 372 } 373 } 374 375 TimeoutHandler timeout; 376 377 private: 378 // SysTime time; 379 // uint _wheelSize; 380 uint _circle; 381 uint _now = 0; 382 }